Skip to main content Link Menu Expand (external link) Document Search Copy Copied

< Back

Lab 5: Simulated Interrupt Example

Raw: xintc_example.c

/******************************************************************************
 *
 * Copyright (C) 2002 - 2014 Xilinx, Inc.  All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * XILINX BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * Except as contained in this notice, the name of the Xilinx shall not be used
 * in advertising or otherwise to promote the sale, use or other dealings in
 * this Software without prior written authorization from Xilinx.
 *
 ******************************************************************************/

/******************************************************************************
 *
 * @file xintc_example.c
 *
 * This file contains a design example using the Interrupt Controller driver
 * (XIntc) and hardware device. Please reference other device driver examples to
 * see more examples of how the intc and interrupts can be used by a software
 * application.
 *
 * This example shows the use of the Interrupt Controller both with a PowerPC
 * and MicroBlaze processor.
 *
 * Revision:
 * 
 * Author: Tendayi Kamucheka (ftendayi@gmail.com)
 * Date:   09/26/2024
 * Changes:
 *         - Replaced XPAR_INTC_0_DEVICE_ID with constant (0) since 
 *           param no longer generated by default in `xparameters.h`.
 *         - Added notes to clarify XIntc API usage and behaviours.  
 ******************************************************************************/

/***************************** Include Files *********************************/

#include "xparameters.h"
#include "xstatus.h"
#include "xintc.h"
#include "xil_exception.h"
#include "xil_printf.h"
#include "xil_types.h"

/************************** Constant Definitions *****************************/

/*
 * Under the hood, the tools create a list of all Interrupt controllers
 * present in the block design. In our case, there's only one present
 * (axi_intc_0), the device id will be (0).  
 */
#define INTC_DEVICE_ID 0 

/*
 *  This is the Interrupt Number of the Device whose Interrupt Output is
 *  connected to the Input of the Interrupt Controller.
 *  - Even though we're only simulating the interrupt with this example,
 *    we'll connect the Interrupt controller to AXI_GPIO_0.
 *  - The interrupt id we're using came from `xparameters.h`. These ids
 *    are generated based the order you connected your interrupt sources
 *    to the Concat IP.
 *    i.e.:
 * 
 *    (0) axi_timer_0 ---\
 *                        ---> Concat ---> axi_intc_0
 *    (1) axi_gpio_0  ---/
 */
#define INTC_DEVICE_INT_ID XPAR_FABRIC_XGPIO_0_INTR

/**************************** Type Definitions *******************************/

/***************** Macros (Inline Functions) Definitions *********************/

/************************** Function Prototypes ******************************/

int IntcExample(u16 DeviceId);

int SetUpInterruptSystem(XIntc *XIntcInstancePtr);

void DeviceDriverHandler(void *CallbackRef);

/************************** Variable Definitions *****************************/

static XIntc InterruptController; /* Instance of the Interrupt Controller */

/*
 * Create a shared variable to be used by the main thread of processing and
 * the interrupt processing
 */
volatile static int InterruptProcessed = FALSE;

/*****************************************************************************/
/**
 *
 * This is the main function for the Interrupt Controller example.
 *
 * @param None.
 *
 * @return XST_SUCCESS to indicate success, otherwise XST_FAILURE.
 *
 * @note  None.
 *
 ****************************************************************************/
int main(void)
{
  int Status;

  /*
   * Run the Intc example , specify the Device ID generated in
   * xparameters.h
   */
  Status = IntcExample(INTC_DEVICE_ID);
  if (Status != XST_SUCCESS)
  {
    xil_printf("Intc Example Failed\r\n");
    return XST_FAILURE;
  }

  xil_printf("Successfully ran Intc Example\r\n");
  return XST_SUCCESS;
}

/*****************************************************************************/
/**
 *
 * This function is an example of how to use the interrupt controller driver
 * component (XIntc) and the hardware device.  This function is designed to
 * work without any hardware devices to cause interrupts.  It may not return
 * if the interrupt controller is not properly connected to the processor in
 * either software or hardware.
 *
 * This function relies on the fact that the interrupt controller hardware
 * has come out of the reset state such that it will allow interrupts to be
 * simulated by the software.
 *
 * @param DeviceId is Device ID of the Interrupt Controller Device,
 *  typically XPAR_<INTC_instance>_DEVICE_ID value from xparameters.h.
 *  NB: XPAR_<INTC_instance>_DEVICE_ID is NOT defined in xparameters.h.
 *
 * @return XST_SUCCESS to indicate success, otherwise XST_FAILURE.
 *
 * @note  Since the device id was not automatically generated, we defined if
 *        above as XINC_DEVICE_ID = 0.
 *
 ******************************************************************************/
int IntcExample(u16 DeviceId)
{
  int Status;

  /*
   * Initialize the interrupt controller driver so that it is ready to
   * use.
   *
   * NB: The device id is used to determine which interrupt controller
   *     to initialize. This is only necessary when there are multiple
   *     interrupt controllers present in the block design. Here,
   *     we're only using one interrupt controller, so the device id
   *     is set to 0.
   */
  Status = XIntc_Initialize(&InterruptController, DeviceId);
  if (Status != XST_SUCCESS)
  {
    return XST_FAILURE;
  }

  /*
   * Perform a self-test to ensure that the hardware was built
   * correctly.
   */
  Status = XIntc_SelfTest(&InterruptController);
  if (Status != XST_SUCCESS)
  {
    return XST_FAILURE;
  }

  /*
   * Setup the Interrupt System.
   */
  Status = SetUpInterruptSystem(&InterruptController);
  if (Status != XST_SUCCESS)
  {
    return XST_FAILURE;
  }

  /*
   *  Simulate the Interrupt.
   */
  Status = XIntc_SimulateIntr(&InterruptController, INTC_DEVICE_INT_ID);
  if (Status != XST_SUCCESS)
  {
    return XST_FAILURE;
  }

  /*
   * Wait for the interrupt to be processed, if the interrupt does not
   * occur this loop will wait forever.
   * NB: The software interrupt has already been triggered by the call to
   *     `XIntc_SimulateIntr` above and the connected interrupt handler
   *     has already set InterruptProcessed = TRUE.  
   */
  while (1)
  {
    /*
     * If the interrupt occurred which is indicated by the global
     * variable which is set in the device driver handler, then
     * stop waiting.
     */
    if (InterruptProcessed)
    {
      break;
    }
  }

  return XST_SUCCESS;
}

/******************************************************************************/
/**
 *
 * This function connects the interrupt handler of the interrupt controller to
 * the processor.  This function is separate to allow it to be customized for
 * each application.  Each processor or RTOS may require unique processing to
 * connect the interrupt handler.
 *
 * @param None.
 *
 * @return None.
 *
 * @note  None.
 *
 ****************************************************************************/
int SetUpInterruptSystem(XIntc *XIntcInstancePtr)
{
  int Status;

  /*
   * Connect a device driver handler that will be called when an interrupt
   * for the device occurs, the device driver handler performs the
   * specific interrupt processing for the device.
   */
  Status = XIntc_Connect(XIntcInstancePtr, INTC_DEVICE_INT_ID,
                         (XInterruptHandler)DeviceDriverHandler,
                         (void *)0);
  if (Status != XST_SUCCESS)
  {
    return XST_FAILURE;
  }

  /*
   * Start the interrupt controller such that interrupts are enabled for
   * all devices that cause interrupts, specify simulation mode so that
   * an interrupt can be caused by software rather than a real hardware
   * interrupt.
   */
  Status = XIntc_Start(XIntcInstancePtr, XIN_SIMULATION_MODE);
  if (Status != XST_SUCCESS)
  {
    return XST_FAILURE;
  }

  /*
   * Enable the interrupt for the device and then cause (simulate) an
   * interrupt so the handlers will be called.
   */
  XIntc_Enable(XIntcInstancePtr, INTC_DEVICE_INT_ID);

  /*
   * Initialize the exception table.
   */
  Xil_ExceptionInit();

  /*
   * Register the interrupt controller handler with the exception table.
   */
  Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
                               (Xil_ExceptionHandler)XIntc_InterruptHandler,
                               XIntcInstancePtr);

  /*
   * Enable exceptions.
   */
  Xil_ExceptionEnable();

  return XST_SUCCESS;
}

/******************************************************************************/
/**
 *
 * This function is designed to look like an interrupt handler in a device
 * driver. This is typically a 2nd level handler that is called from the
 * interrupt controller interrupt handler.  This handler would typically
 * perform device specific processing such as reading and writing the registers
 * of the device to clear the interrupt condition and pass any data to an
 * application using the device driver.  Many drivers already provide this
 * handler and the user is not required to create it.
 *
 * @param CallbackRef is passed back to the device driver's interrupt
 *  handler by the XIntc driver.  It was given to the XIntc driver
 *  in the XIntc_Connect() function call.  It is typically a pointer
 *  to the device driver instance variable if using the Xilinx
 *  Level 1 device drivers.  In this example, we do not care about
 *  the callback reference, so we passed it a 0 when connecting the
 *  handler to the XIntc driver and we make no use of it here.
 *
 * @return None.
 *
 * @note  None.
 *
 ****************************************************************************/
void DeviceDriverHandler(void *CallbackRef)
{
  /*
   * Indicate the interrupt has been processed using a shared variable.
   */
  InterruptProcessed = TRUE;
  xil_printf("Interrupt occurred!\n");
}