Home > Community > Blogs > System Design and Verification > arm generic interrupt controller architecture howto
Login with a Cadence account.
Not a member yet?
Create a permanent login account to make interactions with Cadence more conveniennt.

Register | Membership benefits
Get email delivery of the System Design and Verification blog (individual posts).


* Required Fields

Recipients email * (separate multiple addresses with commas)

Your name *

Your email *

Message *

Contact Us

* Required Fields
First Name *

Last Name *

Email *

Company / Institution *

Comments: *

ARM Generic Interrupt Controller HOWTO

Comments(2)Filed under: ARM, SystemC, System Design and Verifcation, Virtual System Platform, ARM Generic Interrupt Controller, howto, GIC, Cortex-A9, Generic Interrupt Controller, Cortex-A, Wadikar

Way back in 2004, I wrote a book called Co-Verification of Hardware and Software for ARM SoC Design. At that time the world revolved around AHB and the ARM926EJ-S was a popular CPU. All ARM CPUs used two interrupt signals, nIRQ and nFIQ. The nIRQ signal is the normal interrupt request and nFIQ is the fast interrupt request. The bus signals for these two interrupts are active low signals, so driving the signal low indicates an interrupt. The ARM architecture defined exception addresses as shown the table below. 


For verification engineers developing diagnostic test software, this was pretty much all you needed to know to understand the how interrupts work. The nIRQ signal could be asserted and the software would jump to the exception vector shown the table. The only variant is if the VINITHI signal is high during reset; then the exception vectors start at 0xFFFF0000 instead of 0x0. There wasn't all that much more to say in the book.

Now we are in 2011, and the ARM926EJ-S is called a "Classic Processor" by ARM and has been replaced in popularity by the Cortex-A9, and more specifically the Cortex-A9 MPCore with 2 or 4 cores. Such multi-core ARM processors use the Generic Interrupt Controller Architecture, also known as GIC. As you can guess the world of interrupts with a GIC is much more complex than it was in the good old days for engineers writing software diagnostic tests.

To help describe the situation, I turned to Praveen Wadikar, an engineer on the Cadence Virtual System Platform team for some insights. Praveen is a member of the modeling team creating models for Virtual Platforms, many of which include ARM Cortex-A series processors in them.

Following is what Praveen had to share on the topic.

Praveen's GIC HOWTO

The ARM GIC is primarily used in the Cortex-A MPCore series processors, but it not specific to any processor. The ARM GIC is part of the ARM processor private bus interface. The GIC is a centralized resource for supporting and managing interrupts in a system that includes at least one processor. GIC architecture splits logically into a Distributor block and one or more CPU Interface blocks. More details about the GIC can be found in the ARM® Generic Interrupt Controller Architecture Specification.

In this short introduction, the security and pre-emption features supported by GIC are not discussed. Instead this focuses on providing a HOWTO introduction to configure the GIC so that an interrupt from a peripheral is detected by GIC and sent to the appropriate processor. This basic setup is frequently needed when developing new Virtual Platform models and running software tests on an ARM CPU to confirm proper operation of the new peripheral model.

Configuration Steps

The first thing to keep in mind is that the GIC is part of ARM private bus and hence the base address is determined by something called peripheral base, the base address of the internal MPCore peripherals. The GIC distributor base address DISTBASE = PERIPHBASE + 0x1000 and CPU interface base is CPUBASE = PERIPHBASE + 0x100.

Note that the GIC works on interrupt IDs and not the physical IRQS lines. Interrupt ID 0 to 15 is usually used for S/W interrupts. In most of the cases the physical line IRQS[0]  will correspond to interrupt ID 32. This is essential because all the enable/disable and other configuration is based on the function ID.

Ensure that the distributor and CPU interface is not enabled (register at offset 0). After reset the distributor and CPU interface are disabled. If the distributor/cpu interface is enabled then the write is not permitted to other registers in distributor or CPU interface respectively

Read the distributor Interrupt Controller Type Register (ICDICTR). The field ITLinesNumber indicates the maximum number of interrupts that the GIC supports. If this field is 0x3 then max interrupts supported is 32(N+1) = 128. i.e. function ID 0 to 127. Ensure that the interrupt line that is connected to GIC falls within function ID 0 to 127 (i.e. physical IRQS line 0 to 96).

Enable the interrupt set enable register (ICDISERn). So if interrupt ID (N) 77 is to be configured then offset of ICDISERn = 0x100 + (4*M), where M = N/32 (integer division).  The bit number of the required Set-enable bit in this register is N MOD 32

Configure Interrupt Processor Targets Registers (ICDIPTRn). This register determines to which processor the interrupt needs to be sent. For interrupt ID N, when DIV and MOD are the integer division and modulo operations:

  • the corresponding ICDIPTR number, M, is given by M = N DIV 4
  • the offset of the required ICDIPTR is (0x800 + (4*M))
  • the byte offset of the required Priority field in this register is N MOD 4, where:
    • byte offset 0 refers to register bits [7:0]
    • byte offset 1 refers to register bits [15:8]
    • byte offset 2 refers to register bits [23:16]
    • byte offset 3 refers to register bits [31:24]

Set Interrupt configuration register ICDICFRn to configure the interrupt to be level sensitive or edge sensitive. Each Interrupt ID has 2 bit field. Bit0 of the bitfield is reserved and Bit1 = 0 => level sensitive. This bit is not programmable for S/W interrupts.

Configure in CPU interface register map, Interrupt Priority Mask Register (ICCPMR). A value of 0xffff means lowest priority. Any priority higher than the value programmed in this register is serviced.

Enable CPU interface and Distributor. This is all that is needed to setup interrupt delivery.

Example to Enable an Interrupt

This example uses Interrupt ID 76 (IRQS[44]) is to be enabled. In this system the PERIPHBASE = 0x2c0c0000, DISTBASE = 0x2c0c1000 and CPUBASE=0x2c0c0100. For ARM models used in Virtual Platforms the PERIPHBASE is set at the start of simulation via a model parameter. It's important this is done correctly or none of the GIC accesses will work correctly.

// Set Interrupt Enable Registers. Offset = 0x100 + dec_to_hex(76/32*4) = 0x108
hw_write(0x2c0c1000 + 0x108, 0xFFFFFFFF);

// Set ICDIPTRn Registers to cpu0. Offset = 0x800 + dec_to_hex(76/4*4) =  0x84c
hw_write(0x2c0c1000 + 0x84C, 0x01010101);

// Set Interrupt Configuration Registers (ICDICFR0) to level sensitive. Offset = 0xC00 + dec_to_hex(76/16 *4) = 0xC10.
hw_write(0x2c0c1000 + 0xC10, 0x0);  

// Priority Mask. Enable interrupts for lowest priority also
hw_write(0x2c0c0100 + 0x4, 0xFFFF);   

// enable signaling of interrupt
hw_write(0x2c0c0100 + 0x0, 0x3);   

// Configure the Distributor Control Register (ICDDCR) to send pending interrupts to cpus
hw_write(0x2c0c1000 + 0x000, 0x1);  

So any peripheral that generates an interrupt and whose interrupt line is connected to IRQS[44] , the GIC will detect and send it to CPU0.


Cool! Thanks, Praveen. Even the simplest case takes some knowledge just to deliver an interrupt to the CPU from a Virtual Platform peripheral model under development. The most difficult part is that if any of the steps are not correct the result is no interrupt and more head scratching.

Virtual System Platform provides good visualization into the GIC registers in Cortex-A series processors. Below is a screenshot showing the register viewer of some of the GIC registers. This allows detailed debugging of such hardware diagnostics to confirm proper operation.When the built-in software debugger is used to step through the code programming the GIC the changed values are highlighted in red.



We hope this simple HOWTO will help engineers designing hardware tests for peripherals to have an easier time understanding what needs to be done to check that a peripheral is working.

Jason Andrews


By Hai Nguyen on March 20, 2012
Thank you alot. It's very useful for me.

By Hai Nguyen on March 21, 2012
I tried to enable interrupt SPI#85 as your instruction (in my case PERIPHBASE = 0xF000_0000):
/ Set Interrupt Enable Registers. Offset = 0x100 + dec_to_hex(85/32*4) = 0x108
*((unsigned long*)(0xF0001000 + 0x108)) =  0xFFFFFFFF;
// Set ICDIPTRn Registers to cpu0. Offset = 0x800 + dec_to_hex(85/4*4) =  0x854
*((unsigned long*)(0xF0001000 + 0x854)) =  0x01010101;
// Set Interrupt Configuration Registers (ICDICFR0) to level sensitive. Offset = 0xC00 + dec_to_hex(85/16 *4) = 0xC14.
*((unsigned long*)(0xF0001000 + 0xC14)) =  0x0000;  
// Priority Mask. Enable interrupts for lowest priority also
*((unsigned long*)(0xF0000100 + 0x4)) =  0xFFFF;  
// enable signaling of interrupt
*((unsigned long*)(0xF0000100 + 0x0)) =  0x3;  
// Configure the Distributor Control Register (ICDDCR) to send pending interrupts to cpus
*((unsigned long*)(0xF0001000)) = 0x0001;  
But it seems that I can write only to :
+ Configure the Distributor Control Register (ICDDCR)
+  Priority Mask Register
The value of other registers are not changed.
I tried using Lauterbatch to change those registers but it still not affect.
Did I missed some setting before using those register?

Leave a Comment

E-mail (will not be published)
 I have read and agree to the Terms of use and Community Guidelines.
Community Guidelines
The Cadence Design Communities support Cadence users and technologists interacting to exchange ideas, news, technical information, and best practices to solve problems and get the most from Cadence technology. The community is open to everyone, and to provide the most value, we require participants to follow our Community Guidelines that facilitate a quality exchange of ideas and information. By accessing, contributing, using or downloading any materials from the site, you agree to be bound by the full Community Guidelines.