• AVR Freaks

Helpful ReplyHot!Disable/Enable Global Interrupts

Page: 12 > Showing page 1 of 2
Author
aschen0866
Super Member
  • Total Posts : 4573
  • Reward points : 0
  • Joined: 2006/01/08 22:18:32
  • Location: San Diego
  • Status: offline
2006/03/22 18:21:53 (permalink)
0

Disable/Enable Global Interrupts

I am currently using these two steps to disable/enable global interrupt control bit in the INTCON register during a critical region of code.

void Foo( void )
{
   INTCONbits.GIE = FALSE; // Disable global interrupt

   // A critical region of code

   INTCONbits.GIE = TRUE; // Enable global interrupt

}

I can get by with this for now, but I am wondering if there is a better way to handle this since the approach I am taking would have to assume that the global interrupt bit must be set when Foo() is called. I can't really check INTCONbits.GIE or save INTCON register and then disable global interrupt because these steps are not atomic operations.

So, how can I protect a critical region assuming the global interrupt could have already been off before hitting the critical region or the ISR has the potential of altering INTCON itself?
post edited by aschen0866 - 2006/03/22 18:31:28
#1
kalpak
Super Member
  • Total Posts : 3265
  • Reward points : 0
  • Joined: 2004/03/12 23:01:40
  • Location: India
  • Status: offline
RE: Disable/Enable Global Interrupts 2006/03/22 19:22:45 (permalink)
0
Where ever else you set or reset the Global flag, you could save a copy of it. So in the critical part of the code you could blindly disable it on entry and before exiting you could just use that copy to restore the status.
#2
Guest
Super Member
  • Total Posts : 80502
  • Reward points : 0
  • Joined: 2003/01/01 00:00:00
  • Location: 0
  • Status: online
RE: Disable/Enable Global Interrupts 2006/03/22 19:28:11 (permalink)
0
Depending on the PIC core, neither bcf INTCON.GIE is atomic.
An interrupt can occur at the very same clock cycle that the bcf GIE instruction is fetched, then the interrupt is recognized, the instruction is executed, the interrupt is serviced, and the RETFIE enables the interrupts again, exposing your critical section.
#3
aschen0866
Super Member
  • Total Posts : 4573
  • Reward points : 0
  • Joined: 2006/01/08 22:18:32
  • Location: San Diego
  • Status: offline
RE: Disable/Enable Global Interrupts 2006/03/22 20:52:39 (permalink)
0
ORIGINAL: kalpak

Where ever else you set or reset the Global flag, you could save a copy of it. ....

 
Not sure if I understand you correctly, but are you talking about saving INTCON register or INTCONbits.GIE bit? You can't really save INTCON, because interrupt can come in after you save INTCON but before you disable INTCON.GIE and this interrupt happens to change some of the other bits in INTCON.
 
To [j_doin]
 
I am using 18F8680. My experience with PIC is limited and I should read the datasheet more, but how would you write the code to protect a critical region?  Is there a way to save only INTCON.GIE, then disable INTCON.GIE and then at the end of the critical region restore INTCON.GIE?
#4
kalpak
Super Member
  • Total Posts : 3265
  • Reward points : 0
  • Joined: 2004/03/12 23:01:40
  • Location: India
  • Status: offline
RE: Disable/Enable Global Interrupts 2006/03/22 21:34:32 (permalink)
0
Elaborating: If in some function foo you are setting GIE, just save a copy of that bit in another variable. So before the execution reaches that critical part, you have a copy of the GIE flag, which you can use at the end of the critical part where you must restore the GIE status.
#5
jtemples
عُضْوٌ جَدِيد
  • Total Posts : 11930
  • Reward points : 0
  • Joined: 2004/02/13 12:31:19
  • Location: Southern California
  • Status: offline
RE: Disable/Enable Global Interrupts 2006/03/22 22:58:46 (permalink)
0
ORIGINAL: j_doin

Depending on the PIC core, neither bcf INTCON.GIE is atomic.
An interrupt can occur at the very same clock cycle that the bcf GIE instruction is fetched, then the interrupt is recognized, the instruction is executed, the interrupt is serviced, and the RETFIE enables the interrupts again, exposing your critical section.

I don't think that's true except on some very, very old 16C6x chips.
#6
Guest
Super Member
  • Total Posts : 80502
  • Reward points : 0
  • Joined: 2003/01/01 00:00:00
  • Location: 0
  • Status: online
RE: Disable/Enable Global Interrupts 2006/03/23 08:49:29 (permalink) ☄ Helpfulby surreal42 2020/01/08 07:22:24
0
ORIGINAL: jtemples

ORIGINAL: j_doin

Depending on the PIC core, neither bcf INTCON.GIE is atomic.
An interrupt can occur at the very same clock cycle that the bcf GIE instruction is fetched, then the interrupt is recognized, the instruction is executed, the interrupt is serviced, and the RETFIE enables the interrupts again, exposing your critical section.

I don't think that's true except on some very, very old 16C6x chips.

 
That is true for ALL PIC16Cxxx and PIC16Fxxx parts that I have tested so far (16C774, 16C73, 16F873, 16F877). I regard this as a architecture characteristic, not as a silicon bug. Several datasheets and ApNotes (sorry, I can't remember if they all do) for PIC16 parts mention this, and offer several algorithms to circumvent this. The best algorithm, in my opinion, it to keep a 'shadow GIE' bit in shared RAM, and check the 'shadow GIE' before executing a RETFIE at the ISR. If the 'shadow GIE' is clear, you execute a RETURN instead.
 
My INTEN/INTDIS macros maintain the 'shadow GIE' bit. A critical section uses INTPROTECT and INTRESTORE, to save the previous state of GIE before entering the critical section, asserting GIE low, and restoring the previous state of the GIE at end of the critical section.
 
I think that PIC18F devices do not have this issue, but I nevertheless use it on them also.
 
 
#7
chrislev
Super Member
  • Total Posts : 318
  • Reward points : 0
  • Joined: 2005/02/18 11:04:37
  • Location: Kaysville, Utah, USA
  • Status: offline
RE: Disable/Enable Global Interrupts 2006/03/23 08:59:33 (permalink) ☄ Helpfulby surreal42 2020/01/08 07:22:31
0

ORIGINAL: j_doin

Depending on the PIC core, neither bcf INTCON.GIE is atomic.
An interrupt can occur at the very same clock cycle that the bcf GIE instruction is fetched, then the interrupt is recognized, the instruction is executed, the interrupt is serviced, and the RETFIE enables the interrupts again, exposing your critical section.


In assembler you can handle this by doing something like this:

      ; Mask TMR0 int.
      bcf INTCON,T0IE
     
      ; Make sure pending timer interrupt is cleared
      bcf INTCON,T0IF    ; Clear pending TMR0 interrupt
      btfsc INTCON,T0IF
      goto $-2

For GIE and in C, the following code should ensure that all interrupts are disabled before your critical section executes. This code will also take care of re-enabling GIE only if it was enabled in the first place.:

// Disable interrupts
if (INTCONbits.GIE == 1)
{
    GIESet = 1;
    // Make sure that GIE is really 0
    while (INTCONbits.GIE == 1) INTCONbits.GIE == 0;
}

// Critical  section code

// Re-enable interrupts (if necessary)
if (GIESet == 1) INTCONbits.GIE = 1;



Since I'm new to the 18 series chips and C, I'd want to take a look at the generated assember code before using this blindly but off-hand it should work.

-Chris



#8
Guest
Super Member
  • Total Posts : 80502
  • Reward points : 0
  • Joined: 2003/01/01 00:00:00
  • Location: 0
  • Status: online
RE: Disable/Enable Global Interrupts 2006/03/23 09:07:52 (permalink)
0
Looping for GIE clearing is just one of the forms to circumvent the issue I was talking about.
 
#9
jtemples
عُضْوٌ جَدِيد
  • Total Posts : 11930
  • Reward points : 0
  • Joined: 2004/02/13 12:31:19
  • Location: Southern California
  • Status: offline
RE: Disable/Enable Global Interrupts 2006/03/23 10:05:44 (permalink)
0
ORIGINAL: j_doin

That is true for ALL PIC16Cxxx and PIC16Fxxx parts that I have tested so far (16C774, 16C73, 16F873, 16F877). I regard this as a architecture characteristic, not as a silicon bug.

Are you sure about the 16F87x? This "feature" is documented in 16Cx datasheets, but was supposedly removed in the 16Fxx parts (and isn't in their datasheets).
#10
Guest
Super Member
  • Total Posts : 80502
  • Reward points : 0
  • Joined: 2003/01/01 00:00:00
  • Location: 0
  • Status: online
RE: Disable/Enable Global Interrupts 2006/03/23 10:23:44 (permalink)
0
Yes, I scanned all the PIC16F datasheets for this, and the 'shadow' GIE is not mentioned in any of them.
I have tested this on 16F873 and 16F877 about 3 years ago, when a co-worker started to have interrupt problems in his code. It happened that he simply cleared/set GIE to protect the critical sections. When I implemented the shadow interlock, the interrupt 'leakage' was fixed. We engaged in a heated debate then about this, and I tested the concept in a system I was working at the time, with heavy interrupt load for a precision PID motor control loop. I could make my system to fail by removing the GIE interlocking mechanism. Since then, my macro library has been used in a number of other projects, and it uses this GIE interlock.
 
One of the issues we have raised is that this seems to happen more when you have heavy load of external async interrupts, and seems not to happen when you have only internal sync interrupts. I could not determine this, but he used to have other systems with a single timer tick interrupt that never failed.
 
 
 
#11
jtemples
عُضْوٌ جَدِيد
  • Total Posts : 11930
  • Reward points : 0
  • Joined: 2004/02/13 12:31:19
  • Location: Southern California
  • Status: offline
RE: Disable/Enable Global Interrupts 2006/03/23 10:55:02 (permalink)
0
Interesting...did you report your findings to Microchip?
#12
Guest
Super Member
  • Total Posts : 80502
  • Reward points : 0
  • Joined: 2003/01/01 00:00:00
  • Location: 0
  • Status: online
RE: Disable/Enable Global Interrupts 2006/03/23 11:13:20 (permalink)
0
Not really. Actually, this never occured to me, as I always regarded it as a side effect of the PIC16F silicon implementation, like an architectural characteristic.
If the current fetched instruction will be finished execution before the interrupt is vectored, then the bcf GIE instruction can be interrupted and the interrupt will still be vectored, then the RETFIE will return with the GIE set. A simple way to avoid this (that's what I do) is to test the GIE copy and either return with RETURN or RETFIE at the ISR end.
 
Since the interrupt response is already started when the instruction is fetched, the logic would have to drop the interrupt processing if the outcome of the fetched instruction results in a GIE==0. While this is possible, I always thought it was never implemented in PIC16Fs.
 
Now that this is brought up again, I think I will test it again.
 
 
 
 
#13
aschen0866
Super Member
  • Total Posts : 4573
  • Reward points : 0
  • Joined: 2006/01/08 22:18:32
  • Location: San Diego
  • Status: offline
RE: Disable/Enable Global Interrupts 2006/03/23 12:26:56 (permalink)
0
       
ORIGINAL: chrislev




// Disable interrupts
if (INTCONbits.GIE == 1)
{
GIESet = 1;

             // <--- An ISR could take place here...
// Make sure that GIE is really 0
while (INTCONbits.GIE == 1) INTCONbits.GIE == 0;
}

// Critical  section code

// Re-enable interrupts (if necessary)
if (GIESet == 1) INTCONbits.GIE = 1;




If a rule is made saying that no ISR will ever disable GIE, then your approach will work because otherwise an ISR could happen right after you set GIESet = 1 and this ISR disables GIE bit. Now you'll have what j_dion mentioned "interrupt leakage" problem.

post edited by aschen0866 - 2006/03/23 12:28:57
#14
Guest
Super Member
  • Total Posts : 80502
  • Reward points : 0
  • Joined: 2003/01/01 00:00:00
  • Location: 0
  • Status: online
RE: Disable/Enable Global Interrupts 2006/03/23 12:32:33 (permalink)
0
So what if a ISR happens there? The interrupt will resume operation before the GIE while() loop. The critical section would run with the interrupts disabled, and at exit, the GIE would be restored to the GIESet state.
 
 
#15
aschen0866
Super Member
  • Total Posts : 4573
  • Reward points : 0
  • Joined: 2006/01/08 22:18:32
  • Location: San Diego
  • Status: offline
RE: Disable/Enable Global Interrupts 2006/03/23 12:38:05 (permalink)
0
ORIGINAL: j_doin

So what if a ISR happens there? The interrupt will resume operation before the GIE while() loop. The critical section would run with the interrupts disabled, and at exit, the GIE would be restored to the GIESet state.



 
The assumption is that this resumed ISR did not disable GIE, otherwise the GIESet doesn't reflect the true state of INTCON.GIE. 
#16
Guest
Super Member
  • Total Posts : 80502
  • Reward points : 0
  • Joined: 2003/01/01 00:00:00
  • Location: 0
  • Status: online
RE: Disable/Enable Global Interrupts 2006/03/23 13:08:47 (permalink)
0
If the INTCON.GIE is set at the foreground code, an interrupt may happen at whatever point you like, and it will be totally transparent to the foreground code. So, the test for (INTCONbits.GIE==1) can be interrupted several times, and it will still be processed as if nothing happened. Then GIESet will be attributed '1', and this can also be interrupted as many times you like.
But when exiting the while() loop, interrupts will be disabled, and the critical section can be run with no problems.

>> ADDED:
 
I was not understanding your point. You say that IF the ISR disables GIE, then the GIESet mechanism will be broken. I agree. But then, why would you disable interrupts in a interrupt handler?
 
I do that (make the ISR do a RETURN) IF the 'shadow GIE' is not '1' at ISR exit. This only happens if a critical section makes INTDIS or INTPROTECT. Thus, I don't need to wait on GIE in a loop, because the ISR will not set GIE again if the 'shadow GIE' is clear.
 
 
post edited by j_doin - 2006/03/23 13:17:12
#17
aschen0866
Super Member
  • Total Posts : 4573
  • Reward points : 0
  • Joined: 2006/01/08 22:18:32
  • Location: San Diego
  • Status: offline
RE: Disable/Enable Global Interrupts 2006/03/23 14:49:09 (permalink)
0
ORIGINAL: j_doin

I was not understanding your point. You say that IF the ISR disables GIE, then the GIESet mechanism will be broken. I agree. But then, why would you disable interrupts in a interrupt handler?



That was a pure academic question I had in mind.

Back to the code that Chris posted, would that be more efficient if I do

void Foo(void)
{
   unsigned char save_intcon;

   ....
   save_intcon = INTCON; // Save the whole SFR instead of just GIE bit
   INTCONbits.GIE = 0;     // Diable the global interrupt contral bit

   // Critical section goes here

   if (save_intcon & 0x80) INTCONbits.GIE = 1;
   ....
}
#18
chrislev
Super Member
  • Total Posts : 318
  • Reward points : 0
  • Joined: 2005/02/18 11:04:37
  • Location: Kaysville, Utah, USA
  • Status: offline
RE: Disable/Enable Global Interrupts 2006/03/23 14:58:25 (permalink)
0
Since it's my code I feel I should reply. But, I don't know. This is a case where I'd look at the generated ASM to see what's going on. In either case, you are burning a char so it's going to come down to the smaller ASM code.

In ASM I generally use bit flags (8 per register) to save space. In C you do can do interesting things with structs and unions to achieve the same results but again, you need to look at the register space / code space trade off. If nobody has the answer I'll try both methods this weekend and look at the ASM output.

-Chris


#19
Guest
Super Member
  • Total Posts : 80502
  • Reward points : 0
  • Joined: 2003/01/01 00:00:00
  • Location: 0
  • Status: online
RE: Disable/Enable Global Interrupts 2006/03/23 15:37:15 (permalink)
0
There is a significant difference between the 2 programs: chris waited in a tight loop to avoid any GIE leakage. The second program just clears GIE. This may not be an issue if there is no way of an external interrupt being triggered at the GIE clear instruction. In any case, the while() is safer.
But I may be paranoid and overzealous about this.
#20
Page: 12 > Showing page 1 of 2
Jump to:
© 2020 APG vNext Commercial Version 4.5