Altera’s Nios'®' II Processor Reference Handbook and the Nios II Software Developer’s Handbook discuss the details of interrupt service routine (ISR) and interrupt request (IRQ) handling.However, they do not relate this to the actual code written.So the intent of this document is to relate these documents to the “real” code and give a few insights.Most IRQ problems have to do with spurious interrupts, or interrupts which get asserted but then go away before the processor has a serviced them.
IMPORTANT NOTE: Interrupts for Nios II are Level sensitive.
Like most RISC-based processors, the Nios II processor has only one “real” interrupt.However, it is capable of accepting 32.Each of the 32 is “ANDED” with the corresponding bit of the ienable register.The result of that is fed into the ipending register so it can be read by the processor. Then the “OR” of all the ipending is then ANDED with the PIE bit.This resulting signal becomes the hardware interrupt.So for the interrupt to be recognized, the corresponding ienable bit must be set in the ienable register, and the PIE bit must also be enabled.See Figure 3-1 in the Nios II Processor Reference Handbook.
Upon reset, the status register is set to zero.Because the PIE bit is a bit in the status register, it too is cleared, disabling all hardware interrupts (except reset which is nonmaskable). After reset, the code immediately jumps to _reset (defined in the SOPC Builder). This code (in components/nios2/altera_nios2/HAL/src/crt0.S) then initializes the instruction cache and then jumps to _start (in components/nios2/altera_nios2/HAL/src/crt0.S). This code then initializes the data cache, copies the code sections that need to moved, zeros out the bss space, sets up the stack address and then jumps to alt_main (in components/nios2/altera_hal/HAL/src/alt_main.c).Here alt_irq_init is called (c:\\\\altera\\\\kits\\\\nios2_60\\\\components\\\\altera_nios2\\\\HAL\\\\inc\\\\sys\\\\alt_irq.h). Alt_irq_init does two things. It zeros out the ienable registers and turns on the PIE bit in the status register.
Then the rest of the Hardware Abstraction Layer (HAL) is initialized by calling alt_syst_init and then main is called.
To register an interrupt, you call the function alt_irq_register (irq#, context, handler) (c:\\\\altera\\\\kits\\\\nios2_60\\\\components\\\\altera_hal\\\\HAL\\\\src\\\\alt_irq_register.c).This function does the following:
status = alt_irq_disable_all ();
alt_irq[id].handler = handler;
alt_irq[id].context = context;
rc = (handler) ? alt_irq_enable (id): alt_irq_disable (id);
This code disables interrupts (PIE bit set low), then it adds the handler and contexts to the arrays meant to keep that info.Then, if the handler is not NULL, it enables the corresponding bit in the ienable status register. Otherwise, it disables it, turning the interrupt handling off.Then it re-enables interrupts by turning the PIE back on.
If an IRQ is asserted and the corresponding ienable register bit is enabled, the interrupt will be passed to the processor.The processor will finish executing the two instructions left in the pipe. Then the processor will start the exception processing. In hardware, the processor will push most of the registers and the return address to the stack (see Exception Processing in the Nios II Processor Reference Handbook), and then jump to the exception location (set in the SOPC Builder).
The first code that it will execute is the alt_exception_entry code (c:\\\\altera\\\\kits\\\\nios2_60\\\\components\\\\altera_nios2\\\\HAL\\\\src\\\\alt_exception_entry.S)
This code will do some stack checking if that was a compile option.Then it will save off the working registers so the ISR doesn’t have to worry about which registers can be used.Additionally if it is determined not to be a hardware interrupt, the code will reread the instruction that caused the exception to determine what type of exception needs to be processes.
Not all of the code is in alt_execption_entry.S.It is scattered among several files.The only way to see how they really fit together is to look at the linker file.Here is how the linker puts things together:
PROVIDE (__ram_exceptions_start = ABSOLUTE(.));
. = ALIGN(0x20);
<a href= "www.emark.co.cc/a>
It starts off with the exceptions.entrysection, which is the code from the first part of the alt_execption_entry.S file that stores all of the registers. Once the processor finishes the .entry code, it drops into the next section.
Then next section exceptions.irqtest has the code for testing what caused the exception. This code is found in c:\\\\altera\\\\kits\\\\nios2_60\\\\components\\\\altera_nios2\\\\HAL\\\\src\\\\alt_irq_entry.S.It tests the PIE bit and the ipending register.If either are zero, it jumps to .Lnot_irq.
Otherwise, the code falls to the next section. Here the alt_irq_handler is called (c:\\\\altera\\\\kits\\\\nios2_60\\\\components\\\\altera_hal\\\\HAL\\\\src\\\\alt_irq_handler.c).When the IRQ code finishes, it returns to sectionexceptions.irqreturnwhich causes the code to go to exception_exit (in alt_execption_entry.S).This restores all the registers and returns to the user code.
If the exception is not a hardware IRQ, the code goes to section exceptions.notirq then to exceptions.softwhere traps and unimplemented instructions are filtered out ( alt_exception_trap.S and alt_software_exception.S).
Finally, if no source has been identified, the code falls into section exceptions.unknown. Here a break will be triggered if the debug core is installed.Otherwise, it will sit in an infinite loop (alt_exception_entry.S).If you want it to ignore the interrupt, you can simply comment out the break or br 0b and the code will continue to the exit.You may also want to set a hardware register or flag to indicate this has happened.
The most common reason for getting to this point is a spurious interrupt, where the IRQ went away before the ISR was called.SignalTap may be your best way to capture spurious interrupts.
Understanding what is going on in the ISR code can help you debug your system.The code is fairly straightforward once you see how it is put together.