ISR lockup

Discussion of issues related specifically to writing code for native mode devices. This includes ZBasic code as well as assembly language code and C code, both inline and standalone.
Post Reply
pjc30943
Posts: 220
Joined: 01 December 2005, 18:45 PM

ISR lockup

Post by pjc30943 »

The following code fragments seem to correctly set up an INT0 ISR:

Code: Select all

	'~~~~~~~~  INTERRUPTS  ~~~~~~~~~
	Register.EICRA = Bx0101_0101				'0101_0101 = any edge of INT0,1,2,3
	Register.EICRB = Bx0101_0101				'any edge of INT4,5,6,7
	Register.EIMSK = Bx0000_0001	'enable INT0
	const INT_MASK as byte = Bx1000_0000	
	Register.SREG = Register.SREG Or INT_MASK	'enable interrupts
	'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Code: Select all

ISR INT0()
	'test of manual ISRs
	putpin loopTogglePin, zxOutputToggle
	'debug.print "0"
	're-enable interrupts:
	#asm
		reti	
	#endasm
		
End ISR

loopTogglePin follows the pulsetrain (250 Hz) fairly well, but after several seconds, fails to do so and flatlines.

Here is what the program's debug output looks like:

Code: Select all

ZBasiZBaZBZBaZBZBaZBasic v2.5.2
ZBaZBZBaZBasic v2.5.2
ZBaZBasic ZBaZBZBZBaZBZBaZBZBaZBasic ZBaZBaZBZBaZBZBaZBZBaZBZBaZBZBaZBZBaZBZBaZBasic v2.5.2
ZBasiZBaZBZBaZBZBaZBZBaZBZBaZBZBaZBZBaZBZBaZBasiZBaZBZBaZBZBaZBZBaZBZBaZBZBaZBZBaZBZBaZBZBaZBasic v2.5.2ZBZBasic�ZBaZBZBaZBZBaZBZBaZBasic v2.5.2
Nothing else is running except for an infinite sleep loop. but clearly I'm getting some sort of reset or lockup (stack issues, etc.). Anyone have thoughts on how to debug this?
Paul
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Re: ISR lockup

Post by dkinzer »

pjc30943 wrote:The following code fragments seem to correctly set up an INT0 ISR:
The reti isn't necessary in the ISR because the compiler includes one automatically (unless you define the ISR with the Naked attribute). Secondly, interrupts are globally enabled by default; you don't need to enable interrupts globally unless you disable them. That said, you should always use an EnableInt() call to enable interrupts instead of trying to manipulate SREG directly.

I made some changes to the code as shown below. This runs with no problem on a ZX-1281n with a debounced switch attached to the INT0 input.

Code: Select all

Const loopTogglePin	as Byte = B.7

ISR INT0()
   'test of manual ISRs
   Call PutPin(loopTogglePin, zxOutputToggle)
End ISR

Sub Main()
   Call PutPin(loopTogglePin, 0)

   '~~~~~~~~  INTERRUPTS  ~~~~~~~~~
   Register.EICRA = Bx0101_0101     '0101_0101 = any edge of INT0,1,2,3
   Register.EICRB = Bx0101_0101     'any edge of INT4,5,6,7
   Register.EIMSK = Bx0000_0001     'enable INT0
End Sub
- Don Kinzer
pjc30943
Posts: 220
Joined: 01 December 2005, 18:45 PM

Post by pjc30943 »

Thanks Don. It works fine now. [It's pretty darn nice how fast native-mode is.]

I'm curious though why this did not work:
Register.SREG = Register.SREG Or INT_MASK
Paul
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

pjc30943 wrote:I'm curious though why this did not work:
Register.SREG = Register.SREG Or INT_MASK
It's not that it didn't work - just that it is unnecessary (since interrupts are enabled by default). Moreover, if you want to unconditionally enable interrupts use this:

Code: Select all

Call EnableInt(&H80)
More often, though, you'll be enabling interrupts after having disabled them. In that case, however, you want to conditionally re-enable interrupts like this:

Code: Select all

Dim sreg as Byte

sreg = DisableInt()
[ do some stuff here that shouldn't be interrupted ]
Call EnableInt(sreg)
The beauty of this code is that interrupts are enabled only if they were initially enabled. This is important if this code in is a subroutine that is called from some code where interrupts have already been disabled and should remain so.

The real culprit in your original code was the reti that you inserted manually. With that present, the resulting AVR assembly language code for the ISR appeared as shown below, annotated with the original source code. The prologue and epilogue code is added automatically by the compiler to ensure that the integrity of the CPU registers is maintained across the execution of the ISR. The reti that you added manually caused the ISR to return prematurely, leaving 15 bytes on the stack and leaving registers modified. Moreover, the two bytes used for the return address were actually the values that should have been restored to r30 and r31. That is undoubtedly the proximate cause of the "resetting" behavior that you observed. In the unlikely case that the incorrectly used return address just happened to be the correct value, the stack imbalance would eventually cause a stack overflow, again leading to the "resetting" behavior.

Code: Select all

'ISR INT0()

' prologue code
push	r1
push	r0
in	r0, 0x3f
push	r0
eor	r1, r1
push	r18
push	r19
push	r20
push	r21
push	r22
push	r23
push	r24
push	r25
push	r26
push	r27
push	r30
push	r31

'Call PutPin(loopTogglePin, zxOutputToggle)
ldi	r22, 0x04
ldi	r24, 0x07
call	putPin

'#asm
'  reti
'#endasm
reti         <--- this is the culprit

' epilogue code
pop	r31
pop	r30
pop	r27
pop	r26
pop	r25
pop	r24
pop	r23
pop	r22
pop	r21
pop	r20
pop	r19
pop	r18
pop	r0
out	0x3f, r0
pop	r0
pop	r1
reti

'End ISR
- Don Kinzer
pjc30943
Posts: 220
Joined: 01 December 2005, 18:45 PM

Post by pjc30943 »

That makes sense; thanks Don.
Paul
Post Reply