I am trying to trigger an interrupt when a push button is pressed but I can't get service_irq to trigger. The push buttons light up green and the _start function seems to work, as far as I can tell. Any ideas what the problem is and how I can fix it?
I am using ARMv7 and the DE1-SoC in CPUlator.
My code:
.org 0x00000000
.equ SVC_MODE_STACK_BASE, 0x3FFFFFFF - 3 // set SVC stack to top of DDR3 memory
.equ IRQ_MODE_STACK_BASE, 0xFFFFFFFF - 3 // set IRQ stack to A9 onchip memory
.equ GIC_CPU_INTERFACE_BASE, 0xFFFEC100
.equ GIC_DISTRIBUTOR_BASE, 0xFFFED000
.equ PUSH_BUTTONS_BASE, 0xff200050
.equ DISPLAYS_BASE, 0xff200020
.equ PUSH_BUTTONS_IRQ, 73
B _start // reset vector
B SERVICE_UND // undefined instruction vector
B SERVICE_SVC // software interrrupt vector
B SERVICE_ABT_INST // aborted prefetch vector
B SERVICE_ABT_DATA // aborted data vector
.word 0 // unused vector
B SERVICE_IRQ // IRQ interrupt vector
B SERVICE_FIQ // FIQ interrupt vector
.text
.global _start
_start:
// Step 1: Set up the stack for Supervisor Mode
ldr sp, =SVC_MODE_STACK_BASE
// Step 2: Configure the GIC
bl CONFIG_GIC
// Step 3: Configure push buttons interrupts
ldr r0, =PUSH_BUTTONS_BASE // Load base address of pushbutton registers
mov r1, #0xF // Enable interrupts for the keys
str r1, [r0, #8] // Write to the appropriate offset to enable interrupts
// Step 4: Enable IRQ interrupts in the processor
cpsie i // Enable IRQ interrupts
// Step 5: Jump to main program loop
B LOOP
LOOP:
B LOOP
SERVICE_IRQ:
PUSH {R0-R7, LR}
/* Read the Interrupt Acknowledge Register (ICCIAR) from the CPU interface */
LDR R4, =GIC_CPU_INTERFACE_BASE
LDR R5, [R4, #0x0C] // read from ICCIAR
// Set the display to 0 to test if service_irq is triggered
LDR R0, =DISPLAYS_BASE
MOV R1, #0b00111111
STR R1, [R0]
B EXIT_IRQ
EXIT_IRQ:
/* Write to the End of Interrupt Register (ICCEOIR) */
STR R5, [R4, #0x10] // write to ICCEOIR
// Restore registers and return from interrupt.
POP {R0-R7, LR}
SUBS PC, LR, #4 // Atomically restores CPSR from SPSR and returns to interrupted instruction.
SERVICE_UND:
B SERVICE_UND
SERVICE_SVC:
B SERVICE_SVC
SERVICE_ABT_DATA:
B SERVICE_ABT_DATA
SERVICE_ABT_INST:
B SERVICE_ABT_INST
SERVICE_FIQ:
B SERVICE_FIQ
CONFIG_GIC:
PUSH {LR}
/* To configure a specific interrupt ID:
* 1. set the target to cpu0 in the ICDIPTRn register
* 2. enable the interrupt in the ICDISERn register */
/* CONFIG_INTERRUPT (int_ID (R0), CPU_target (R1)); */
MOV R1, #1 // this field is a bit-mask; bit 0 targets cpu0
BL CONFIG_INTERRUPT
/* configure the GIC CPU Interface */
LDR R0, =GIC_CPU_INTERFACE_BASE // base address of CPU Interface, 0xFFFEC100
/* Set Interrupt Priority Mask Register (ICCPMR) */
LDR R1, =0xFFFF // enable interrupts of all priorities levels
STR R1, [R0, #0x04]
/* Set the enable bit in the CPU Interface Control Register (ICCICR).
* This allows interrupts to be forwarded to the CPU(s) */
MOV R1, #1
STR R1, [R0]
/* Set the enable bit in the Distributor Control Register (ICDDCR).
* This enables forwarding of interrupts to the CPU Interface(s) */
LDR R0, =GIC_DISTRIBUTOR_BASE // 0xFFFED000
STR R1, [R0]
POP {PC}
CONFIG_INTERRUPT:
PUSH {R4-R5, LR}
/* Configure Interrupt Set-Enable Registers (ICDISERn).
* reg_offset = (integer_div(N / 32) * 4
* value = 1 << (N mod 32) */
LSR R4, R0, #3 // calculate reg_offset
BIC R4, R4, #3 // R4 = reg_offset
LDR R2, =0xFFFED100 // Base address of ICDISERn
ADD R4, R2, R4 // R4 = address of ICDISER
AND R2, R0, #0x1F // N mod 32
MOV R5, #1 // enable
LSL R2, R5, R2 // R2 = value
/* Using the register address in R4 and the value in R2 set the
* correct bit in the GIC register */
LDR R3, [R4] // read current register value
ORR R3, R3, R2 // set the enable bit
STR R3, [R4] // store the new register value
/* Configure Interrupt Processor Targets Register (ICDIPTRn)
* reg_offset = integer_div(N / 4) * 4
* index = N mod 4 */
BIC R4, R0, #3 // R4 = reg_offset
LDR R2, =0xFFFED800 // Base address of ICDIPTRn
ADD R4, R2, R4 // R4 = word address of ICDIPTR
AND R2, R0, #0x3 // N mod 4
ADD R4, R2, R4 // R4 = byte address in ICDIPTR
/* Using register address in R4 and the value in R2 write to
* (only) the appropriate byte */
STRB R1, [R4]
POP {R4-R5, PC}
.end