Basic AT91SAM7 interrupt

I am learning the AT91SAM7S64 using YAGARTO and having trouble getting a simple external interrupt to operate. It seems the interrupts are more difficult than I am used to. Here’s what I have

	disableIRQ();

	// Start the PIO clock
	AT91C_BASE_PMC->PMC_PCER = (1<<AT91C_ID_PIOA);
		
	// Sets up input for IRQ0
	volatile AT91PS_PIO pPIOA = AT91C_BASE_PIOA;
	pPIOA->PIO_PDR = (1<<20); 		// Disables pin from PIO control
	pPIOA->PIO_BSR = (1<<20);		// Makes periperhial B
	pPIOA->PIO_ODR = (1<<20); 		// Makes an input
	pPIOA->PIO_CODR = (1<<20);		// Clears the output
	pPIOA->PIO_PPUER = (1<<20);		// Enables the pullups
	pPIOA->PIO_OWDR = (1<<20);		// Output write disable
	    
							
	// Set up IRQ0
	volatile AT91PS_AIC pAIC = AT91C_BASE_AIC;
	pAIC->AIC_IDCR = (1 << AT91C_ID_IRQ0);								// Disables IRQ0 interrupt in AIC
	pAIC->AIC_SMR[AT91C_ID_IRQ0] =(AT91C_AIC_SRCTYPE_INT_POSITIVE_EDGE);//Sets the INT source and priorty
	pAIC->AIC_SVR[AT91C_ID_IRQ0] = (UINT)irq0_handler;					// Sets the IRQ handler address
	pAIC->AIC_ICCR = (1 << AT91C_ID_IRQ0);								// 
	pAIC->AIC_IDCR = (0 << AT91C_ID_IRQ0);								//
	pAIC->AIC_IECR = (1 << AT91C_ID_IRQ0);								// Enable IRQ0 interrupt
	
	enableIRQ();		// Enable global interrupts

After the interrupt is set-up above, I just go into a never ending while loop for testing. I step through the code, push the button on the Olimex board but the code never goes to the IRQ vector. The IMR is correct with bit 30 set, with the matching IPR value on the next instruction after the button press. Why is the interrupt pending forever? I am noticing that the IVR is reading 0x00 all the time. What am I missing? Thanks for any help.

Off the top of my head, what you have looks right.

I’d probably not mess with CODR and OWDR, since you’re setting the pin to input, but it doesn’t hurt anything to have that in there. Other than a little time and memory.

The following line is a no-op

pAIC->AIC_IDCR = (0 << AT91C_ID_IRQ0);

If you’re not in a privileged mode (ie: you’re in USR mode), the enableIRQ/disableIRQ functions/macros might not do anything.

You’re not clearing the FF bit. That’s fine if it wasn’t set previously, but if it was, for some reason, the FIQ exception would get triggered instead of the IRQ one. Though, if that were the case, you’d see it at least vectoring somewhere, so that probably isn’t your problem. Maybe you already cleared all of the FF bits during initialization.

Did you write to EOICR 8 times during initialization of the AIC before getting here?

The datasheet alludes briefly to a GMSK bit in AIC_DSR. If you find that AIC_DSR is 0x02 or 0x03, that bit is preventing the interrupt from being recognized by the cpu. It might be getting set by your debugger.

Also, since you’re using a debugger, you want to make sure to do a dummy write to AIC_IVR as described in the datasheet under “Protect Mode” if you aren’t already.

Further, for edge-triggered interrupts such as the one you’ve specified here, you will need to clear the pending bit by writing the bitmask to AIC_ICCR or else it will still be pending upon return and the ISR will get called ad infinitum. Again; if you aren’t already.

These are my observations/guesses. What you have here looks fine to me.

The processor mode is set to System (4:0 of CPSR is 11111). Bit 6 (FIQ) is set, and bit 7 (IRQ) is clear.

The AIC_DSR (AIC_DCR for me) reads 0x00. If I enable the protect mode like this:

pAIC->AIC_DCR = (1 << AT91C_AIC_DCR_PROT);							// Enables debug protect mode

then the register reads 0x02. What do you mean by the FF bit?

You’re not clearing the FF bit. That’s fine if it wasn’t set previously, but if it was, for some reason, the FIQ exception would get triggered instead of the IRQ one. Though, if that were the case, you’d see it at least vectoring somewhere, so that probably isn’t your problem. Maybe you already cleared all of the FF bits during initialization.

I’m not writing to the EOICER at all before the initial interrupt is supposed to happen. Do I need to?

Did you write to EOICR 8 times during initialization of the AIC before getting here?

I'm going to slave over the datasheet and bang my head on the wall some more. Thanks for any replies.

I’m not writing to the EOICER at all before the initial interrupt is supposed to happen. Do I need to?

According to the datasheets you need to clear the 8 level deep interrupt stack of the AIC.

Has:
What do you mean by the FF bit?

The bit that is set using AIC_FFER and cleared using AIC_FFDR. This is the Fast Forcing bit, and causes an interrupt to not go through the priority controller and IRQ exception handler and to instead go directly to the FIQ exception handler.

I just set AIC_FFDR to ~0 (all ones) during initialization. The datasheet says that AIC_FFSR initializes to 0, but I wrote my initialization code so that it can run by itself or from a bootloader, and I can’t assume anything about the AIC’s state in the latter case. And then there’s the possibility of processor reset without peripheral reset which I mention below.

Has:
I’m not writing to the EOICER at all before the initial interrupt is supposed to happen. Do I need to?

You very well might, depending on how the hardware initializes on reset or whether it has already been used by something else (like a bootloader) since reset. Also, while I’d have to check to be sure, I’ve got a feeling a processor reset might not even reset the AIC. A peripheral reset would. The distinction would be unimportant for power-on as both resets happen then, but if you’re resetting by writing to RSTC_RCR, it is possible to reset only the processor.

I don’t have any assurance that the hardware initializes with an empty stack, so I do 8 writes to AIC_EOICR, which, since the hardware stack is 8 levels deep (one level per possible priority, I assume), empties it. If the AIC thinks it is servicing an interrupt, it won’t service another of the same or lower priority. You also have to write (anything) to that register once before exiting the ISR. If you don’t, your ISR won’t run again, and neither will any others of the same or lower priority.

Has:
I’m going to slave over the datasheet and bang my head on the wall some more.

Yeah; I do that a lot.

I enabled the debug protect mode like this:

pAIC->AIC_DCR = AT91C_AIC_DCR_PROT;							// Enables debug protect mode

The AIC_DCR register now reads 0x01. Without any other changes made, pressing the IRQ0 button now changes three AIC registers. The AIC_IVR now matches the AIC_SVR[30] with a value of 0x3b4. The IPR and IMR registers match as they did before. The AIC_CISR status register now reads 0x02 indicating the IRQ line is active. Obviously the debug protect mode is needed to view the changes to the AIC registers.

I added the following lines to clear the stack:

	for (i=0; i < 8; i++)
	{
		AT91C_BASE_AIC->AIC_EOICR = 0;
	}

This didn’t seem to change anything, but I’ll leave it anyways. The AIC_FFSR register reads 0x00 so I don’t think I need to do anything with the FF.

Just to make sure, the dummy write to the IVR register happens after the program starts executing the interrupt routine correct? I’m getting closer, but the code still stays in the while loop forever waiting for the interrupt to fire. Any other suggestions?

Here’s the default handler code:

	// Set up the default interrupts handler vectors
	AT91C_BASE_AIC->AIC_SVR[0] = (int) AT91F_Default_FIQ_handler;
	AT91C_BASE_AIC->AIC_SVR[30] = (int) AT91F_Default_IRQ_handler;
	AT91C_BASE_AIC->AIC_SPU  = (int) AT91F_Spurious_handler;

The irq0_handler.c only contains a line of code that turns on an LED and is a breakpoint. This is only for testing, I know there needs to be more there, I’m just mentioning it in case there’s a problem there.

Has:
The AIC_DSR (AIC_DCR for me) reads 0x00. If I enable the protect mode like this:

pAIC->AIC_DCR = (1 << AT91C_AIC_DCR_PROT);							// Enables debug protect mode

then the register reads 0x02.

You’re doing it wrong. At least according to my version of the Atmel header file. It defines AT91C_AIC_DCR_PROT as (0x1 << 0). The shift is already in there. I think what you really want is:

pAIC->AIC_DCR = AT91C_AIC_DCR_PROT;							// Enables debug protect mode

You were inadvertently setting AT91C_AIC_DCR_GMSK (which is 0x1 << 1), which would cause the exact problem I was wondering about even though you hadn’t been experiencing it previously.

Has:
I enabled the debug protect mode like this:

pAIC->AIC_DCR = AT91C_AIC_DCR_PROT;							// Enables debug protect mode
I see you already discovered what I just talked about in another comment.

Has:
The AIC_DCR register now reads 0x01. Without any other changes made, pressing the IRQ0 button now changes three AIC registers. The AIC_IVR now matches the AIC_SVR[30] with a value of 0x3b4. The IPR and IMR registers match as they did before. The AIC_CISR status register now reads 0x02 indicating the IRQ line is active. Obviously the debug protect mode is needed to view the changes to the AIC registers.

Ah yes, it is. Reading the IVR without it changed the state of the AIC. I expected the debugger to set that for you, though, and it apparently didn’t.

Has:
I added the following lines to clear the stack:

	for (i=0; i < 8; i++)
{
	AT91C_BASE_AIC->AIC_EOICR = 0;
}


This didn't seem to change anything, but I'll leave it anyways. The AIC_FFSR register reads 0x00 so I don't think I need to do anything with the FF.

I have gotten the impression that this (the 8 writes) needs to be done, and I do it anyway because of the aforementioned bootloader issue in my particular project.

Regarding FF, it looks like you’re good. So long as you only ever start your application directly after a peripheral reset or you only ever have one particular line (or none) set to FF. In other words, if you can count on it always being 0x00. I set it to 0 in my startup code because I cannot count on it in my environment.

Has:
Just to make sure, the dummy write to the IVR register happens after the program starts executing the interrupt routine correct?

Yeah. I have mine immediately after the read of IVR (I’m not doing that shortcut where you put the magic instruction in the exception table; if you are, you could just do it early in the handler). I’m not using a debugger, but it is there to keep me from getting bitten if I start using one.

Has:
I’m getting closer, but the code still stays in the while loop forever waiting for the interrupt to fire. Any other suggestions?

Here’s the default handler code:

	// Set up the default interrupts handler vectors
AT91C_BASE_AIC->AIC_SVR[0] = (int) AT91F_Default_FIQ_handler;
AT91C_BASE_AIC->AIC_SVR[30] = (int) AT91F_Default_IRQ_handler;
AT91C_BASE_AIC->AIC_SPU  = (int) AT91F_Spurious_handler;


The irq0_handler.c only contains a line of code that turns on an LED and is a breakpoint. This is only for testing, I know there needs to be more there, I'm just mentioning it in case there's a problem there.

irq0_handler.c defines AT91F_Default_IRQ_handler, right? That is the function that just turns on an LED? Do you not have a write to AIC_ICCR in there? You need one for edge triggered interrupts. Without it, you’ll keep executing the interrupt and not spend much if any time in your main loop. Of course, for that to become a problem, we have to get you invoking the routine.

That the bit in AIC_CISR gets set tells me that the AIC is at least trying to dispatch the interrupt. Well, that and AIC_DCR being set correctly now if not before. So the IRQ line in the ARM core should be getting asserted. You’ve already stated that the I bit in the CPSR is clear, so it should be throwing an IRQ exception. How are you handling the IRQ exception?

What happens on your system when you throw an unhandled exception? If the board were rebooting or spinning, would you know?

Hi Has,

I posted to another thread, but I’m not sure that you got it. Would you mind posting an indoor and an outdoor picture taken with the VS6724 so that I can judge the quality?

If you would like to contact me offline, my address is:

p_i_e_t_e_r_@p_i_c_o_n_o_m_i_c.c_o._z_a

Thanks!

Pieter

pieterc - I emailed you the VS6724 pictures.

I found the interrupt problem, and it’s with the debugger. While in instruction stepping mode, the interrupts don’t appear to work. I’m using the Olimex ARM-USB-OCD and Yagarto.

I have a breakpoint at the start of the interrupt handling routine. If I use the play button, the endless while loop executes until I press the button attached to IRQ0. The code then branches to the IRQ0 handler and stops at the breakpoint.

So I guess the question now is how can I step through the instructions one at a time AND have the interrupts operate correctly? Could it be a processor setting, or something in the OCD scripts?

Has:
I found the interrupt problem, and it’s with the debugger.

So I guess the question now is how can I step through the instructions one at a time AND have the interrupts operate correctly? Could it be a processor setting, or something in the OCD scripts?

I was sort of wondering whether your debugger followed interrupts. But I figured it must. It didn’t even occur to me that it would break them altogether. I’m using the “blink LEDs to show me you’re alive” method. I don’t know much about OCD scripts.

We’ve already discussed the only two ways I know of to suppress interrupts (the CSPR and that bit in the AIC), but I’m unfamiliar with the debug hardware in the chip. It could very well be a feature of that. I don’t know that it is, but it could be.

The debug stuff, should you wish to bang your head against it, is in chapter 5 of the ARM7TDMI Technical Reference Manual. There may well be a solution to your problem, but I’m unfortunately not going to be able to be of much use. In scanning that document, nothing has jumped out at me.