Why Does An Interrupt Service Routine (Isr) Need To Save All Register It Uses Including Sreg?
Interrupts and Resources
Interrupts are effective because they simply require action when those are actually necessary. In nearly cases it is not necessary to predict when exactly this will happen. The machinery fifty-fifty allows that several events overlap.But: they need some specific resources to work well:
- They demand a functioning stack.
- Every bit they can occur at whatever time, and if the service routine alters flags in the condition register, they accept to salvage and, at the end, to restore the condition register.
- All other used resource, such as registers, SRAM content, internal hardware etc., all need to be sectional for the interrupt: whenever you alter something, yous'll have to brand certain that these changes are fine and practise not interact with other program uses of such resources.
The stack
Interrupts tin can occur at any fourth dimension. They change the controller'south program counter and so execute dissimilar code. In order to return back to the original place, to before the upshot occurred, they need to store that address somewhere. In a few older AVR types (AT90S1200, ATtiny12) they had an internal storage place for those 16-bit addresses. All newer devices use the stack for this purpose.The stack is located in the SRAM, more than specific: in its last bytes. A stackpointer is used to indicate to that location, and this stackpointer is automatically manipulated. Whenever an interrupt occurs, the 2 accost bytes are written to the stack, and the stackpointer advances two steps backwards. At the end of the interrupt service routine, when the instruction RETI is executed, these two accost bytes are read back to the program counter. Reading automatically advances the stackpointer, and so its original value is reached after RETI.
And then, any interrupt-driven program has to init the stackpointer, once following its RESET. The following assembler lawmaking is to be used for that:
ldi R16,Low(RAMEND) ; Point to the end of SRAM, LSB accost out SPL,R16 ; Init the LSB of the stackpointer .ifdef SPH ; Does the device have an MSB stackpointer? ldi R16,High(RAMEND) ; Point to the cease of SRAM, MSB address out SPH,R16 ; Init the MSB of the stackpointer .endif
The beginning 2 instructions have to be executed in any case. SPL is a port register and holds the stackpointer's lower viii bits. The function Low returns the lower eight bits of the constant RAMEND, which is divers in the def.inc-file of the device (to be included with the .include directive. That is the reason why you lot need the def.inc: RAMEND depends from the device'due south SRAM extend. If your device has more than 32 + (port annals extent) + (SRAM-size) > 256 bytes, it has an additional SPH port register that holds the upper 8 bits of the stackpointer address. The two following instructions only execute in cases that port register SPH exists as constant (equally divers in the def.inc file). If that symbol name does non exist, those two instructions are non exceuted.
To demonstrate the machinery, the post-obit pictures show an interrupt execution in the simulator avr_sim. Simulated is an overflow of TC0 in an ATtiny13.
In the first two instructions the stack arrow of the device is initiated. Information technology points to the final location in the SRAM of the device.
With the side by side four instructions the timer is started in normal mode and with a prescaler value of one. The timer's overflow int has been enabled.
When the timer reaches 256 (and restarts with nada) he requests an interrupt past setting its respective flag bit. If no higher-priority interrupt request is activated, the overflow interrupt volition be executed as next step.
Indeed, the overflow interrupt is now executed.
The stackpointer has been decreased from 0x9F downwardly to 0x9D, two bytes return address have been written to the SRAM'south terminal two positions, and the program counter is on the timer-overflow jump address executing the instruction there.
Afterward the RETI instruction has been executed the stackpointer returned to its previous value, the execution address has been restored and the timer waits for the adjacent overflow.
One time the stack has been initiated for treatment interrupts, you can utilize it for other purposes as well. The instrcution (R)CALL uses it for calls to subroutines: just like in an interrupt the calling address is pushed to the stack and the calling address is written to the program counter. Whenever your subroutine is completed, you can return to the calling address with the education RET. And, similar RETI, the return address is read back from the stack to the program counter.
Some other use of the stack: you can PUSH the content of a register on the stack, and subsequently restore this (or any other) register with POP. This even works with interrupts: whenever those occur they use the next two stack locations for the address to jump dorsum on RETI, no matter where the stackpointer points to when the interrupt starts. As the interrupt return accost is always on the bottom of the stack (the stackpointer advances backwards), after RETI the program finds its pushed stack content at the right place, even if in betwixt an interrupt has been executed.
The stack size can require a few bytes at the terminate of the SRAM. Every bit only one interrupt is executed at a fourth dimension, while others wait for their execution, this size is very small. Fifty-fifty ten interrupts in a row demand only two bytes of stack space. If you utilize many additional PUSHs and POPsouthward you might demand a bit more stack space. If you program in C, your stack might be unnecessarily filled with several useless Buttons by the compiler, but not so in assembler.
Forgetting to init the stack with interrupts enabled yields some funny results. As the stackpointer points to goose egg by default, and as there is no SRAM space where something can exist stored, the interrupt will not be able to store and call up the render accost. It therefore executes, but only once. And never returns back, to where it was earlier the interrupt occurred. All subsequent interrupts besides execute correctly, but also never return. Nice example for a simulator (like avr_sim): this should recognize the missing stackpointer.
Saving/restoring the condition register
One of the resource you take to take care of in case of an interrupt is the status register. This is implemented in AVRs simply once, and then interrupts and normal programme execution both use this resources.Equally interrupts are assynchroneous to normal program execution and can execute at whatever time, fifty-fifty if normal execution is executing something else than the sleep pedagogy, you'll have to relieve the status register at the showtime of whatsoever interrupt service routine and restore its content at the end. This applies only if your service routine changes flags. So a typical ISR looks similar that:
Isr: in R15,SREG ; Save condition register content in a register ; ... execute interrupt service out SREG,R15 ; Restore condition register content reti ; Return from interrupt
In one instance this is a difficult choice: if you want to change the T flag in SREG. The T flag in the condition annals can exist used as a multipurpose flag. It can exist ready with Prepare and cleared with CLT or tin can exist loaded with a unmarried bit in a register with BST or a single bit in a register can exist overwritten with the T flag past BLD. If you have saved SREG and if you lot want to restore it after, modify this flake in the annals you have saved SREG in. Otherwise your modify in SREG will be overwritten at the end of the ISR. Or: if naught else in that routine changes flags: just do not save and restore SREG. The T flag is the reason why AVRs exercise not automatically save and restore the SREG during interrupts. There are dissimilar methods to save and restore SREG.
Nr. | Relieve in | Code | Fourth dimension required Clock cycles | Pro's | Con'southward |
---|---|---|---|---|---|
1 | Register | in R15,SREG [...] out SREG,R15 | ii | Fast | Annals needed |
2 | Stack | push R0 in R0,SREG [...] out SREG,R0 pop R0 | 6 | No annals required | Deadening |
3 | SRAM | sts $0060,R0 in R0,SREG [...] out SREG,R0 lds R0,$0060 | six |
Use of other resources
As interrupts can occur at any time, exist certain to avoid interferences with normal plan execution. A register or an SRAM location that is used by an ISR cannot be used past any other program part. Exist sure that you do non mix appliances hither without being aware what the consequences could be. If you lot demand 1 register or a register pair in an interrupt, handle this as sectional and note that in the annals defintion section as "Used in interrupt X". If you demand information technology only temporarily, name it something with an "i", such as rimp for "Register Interrupt Multi Purpose" or as riTemp. As no interrupt can occur during an ISR execution the same register can be used temporarily in different ISRs.If yous demand to change 16-bit values that are used in an interrupt from exterior the ISR: disable interrupts temporarily with CLI and enable those if you have written both registers or SRAM locations. Otherwise yous adventure than an interrupt in between works with a partial or incomplete and faux value. An example: An interrupt counts downwardly a value in the register pair R25:R24 with SBIW R24,i, eastward. g. for the elapsing of a tone. If you change either R25 or R24 from outside, make certain that no interrupt occurs in betwixt those two. You would end up with a completely imitation elapsing if that would happen.
A special case tin can occur if you accept a fast and a slow interrupt generation mixed or if your flag treatment routine is very long. The 2 or more handling routines then take to ensure that all flag settings are treated and that no flag is missed. That means y'all have to carefully select the what-comes-first and, if necessary, to insert further flag handling routine calls inside your lengthy routine. Just consider your timings and check whether something can get wrong or not.
Some basic recommendations
- Do non forget stack initiation.
- Is the status annals saved and restored in each branching ISR?
- Define registers either exclusively for interrupts or make certain that no interference can occur.
- If used in- and exterior of ISRs: brand a articulate distinction who is writing and who is only reading content, who's the master and who the slave. Take intendance when changing 16 bit values. Do'nt forget re-enabling interrupts if you switched them off temporarily.
- The faster your interrupts follow the more intendance you'll take to have to avoid overruns.
To the top of that page
©2009-2019 past http://world wide web.avr-asm-tutorial.internet
Source: http://www.avr-asm-tutorial.net/avr_en/interrupts/int_resources.html
Posted by: freemanpinhould1981.blogspot.com
0 Response to "Why Does An Interrupt Service Routine (Isr) Need To Save All Register It Uses Including Sreg?"
Post a Comment