CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

INT_GLOBAL vs INT_FAST vs INT_HIGH on 18F parts

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
5440



Joined: 08 Feb 2009
Posts: 11

View user's profile Send private message

INT_GLOBAL vs INT_FAST vs INT_HIGH on 18F parts
PostPosted: Sat Nov 13, 2010 6:35 pm     Reply with quote

I did some reading but have a couple of Q's regarding priority ints on 18F devices.

I am playing with a 18F2585 on CCS 4.106

I think I understand the following:

1) Using INT FAST will allow a high priority int to interrupt a low priority in progress. No context saving is performed as I seen from the listing

2)Using INT HIGH will allow a high priority int to interrupt a low priority in progress, but will generate context saving code is performed as I seen from the listing, 50 cycles or so extra over the FAST option.The compiler needs the statement #device HIGH_INTS=TRUE for priority Ints.

3) Using an interrupt that has no assigned priority (Microchip design) such an INT0 pin, will basically generate code that is the same as if HIGH was used.ex One INT_EXT and one fast int, INT_TIMER1 FAST would generate code that adds the saving overhead.

4) INT_GLOBAL, generates nothing, but the complier expects a user ISR at 0x08 which is the high vector when used (low priority vector is 0x18), else 0x08 is used for all non high priority ints

Q's:

Q1) If one wants the tightest high priority int code, is it best to use INT_Global or use FAST INTs? The user would have to add context saving and check the flags for Global Ints to determine what ints occurred? If so a low int is in progress, what happens when a high priority occurs? Would it just vector back to 0x08? How does it back to the low priority int? Any good example code for something like this?

Q2) If one needed a INT such as EXT (INT0 pin) that is no a priority int, are you stuck using a Global Int as all FAST Int would be handled as HIGH and have the overhead automatically added?

Q3) Is it a good idea to have more than one high priority (HIGH or FAST) enabled at one time? What happens if two high priority ints are pending at the same time?

Q4) If one were to use GLOBAL INTS, what registers really need to be saved if one is doing basic code in the ISR such as saving a CCP ot TMR reg, or inc a counter variable? What is the point of all the saving I seen in the listing file for HIGH INT?

Q5) If one wanted tight code for non priority Ints (compatibility mode), is it GLOBAL INT to only option?

Q6) Is it common practice to mix asm (for save/restore) and C code in the ISR?
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Sun Nov 14, 2010 3:40 am     Reply with quote

If you have _any_ interrupt set as high priority, and also have the standard 'INT' in use, this _always_ becomes a high priority INT. Hence an interrupt source test becomes 'forced'. This is a hardware limitation of the chips, with this interrupt having no 'priority' bit. This is why in your notes, when you used INT_TIMER1 FAST, the saving overhead was generated, since the compiler did this automatically. This is also your Q2 answer. If you want a single high priority interrupt with the minimum overhead, then it is vital to not have INT0 in use as well...
You can only ever have one interrupt flagged as 'FAST', since otherwise the code must test for the source, and the code to do this together with saving the registers, is automatically added.

The amount of saving needed, depends totally on the code _you_ use in the interrupt handler. If you perform array operations, the table pointers have to be saved. Most things will affect the W register, but the RETFIE1 instruction, restores this for you (and the BSR etc..). The only way to really optimise the interrupt handler, is to write the handler code, then go through the assembler listing generated, and note down every register that is used. Then add code to save these.
The compiler defaults to saving everything that it uses, even if the routines using the registers don't exist in the interrupt handler. I have often 'wished' for a compiler option, that checked for what registers were actually used in the interrupt routines, and only saved these. Lacking this, you have to do it yourself.

There is no inherent speed difference between using INT_GLOBAL, or fast INTs. In fact the 'FAST' directive, can be used to generate the exact equivalent to INT_GLOBAL for the high priority interrupts. If you generate a _single_ interrupt handler and flag it as 'FAST', this becomes the 'global' handler for high priority interrupts. You can add code to this to test for what interrupt occurred, and then manually set the priority flag for another interrupt source.

You need to slightly change your thinking about the interrupt vectors. 0008, is the 'high priority' vector address _when interrupt priorities are enabled_, and the 'global' vector address when priorities are disabled. Hence if you define INT_GLOBAL with no high priority vector enabled, the handler goes at address 8. If you wanted to handle both high and low priority interrupts, you would need to write the INT_GLOBAL routine to immediately vector to the high priority handler, and then have a second entry point for the low priority interrupts.
You can still use high priority interrupts with an INT_GLOBAL, but you have to set the priority bits yourself.

Have a look at ex_glint.c, which gives a basic example of handling an interrupt 'fast' using the global declaration.
The below is a small 'snippet' from a handler designed to give minimum latency for a quadrature change, with this needing less registers saved than the other routines:
Code:

#int_global
void myint(void) {
   static int INT_scratch[10];
#ASM
   //Here for maximum speed, I test the RB interrupt - since it is allways
   //enabled, I don't have to test the enable bit
   BTFSS   INTCON,RBIF
   GOTO    NXT
#endasm
   quad();              //Quadrature handler.
#asm
   GOTO    FEXIT        //Use the fast stack for exit
NXT:
   //Now I save the registers needed for the other interrupt handlers
   MOVFF   FSR0L,INT_scratch
   MOVFF   FSR0H,INT_scratch+1
   MOVFF   FSR1L,INT_scratch+2
   MOVFF   FSR1H,INT_scratch+3
   MOVFF   FSR2L,INT_scratch+4
   MOVFF   FSR2H,INT_scratch+5
   MOVFF scratch,INT_scratch+6
   MOVFF   scratch1,INT_scratch+7
   MOVFF   scratch2,INT_scratch+8
   MOVFF   scratch3,INT_scratch+9
   //Test for the external interrupt (power fail).
   BTFSS    INTCON,INT0E
   GOTO     NEXTA
   BTFSC    INTCON,INT0
#endasm
   pfail();
#asm
NEXTA:
   //Now for Timer 2
   BTFSS     PIE1,TMR2
   GOTO      NEXT1
   BTFSC     PIR1,TMR2
#endasm
   TIMER2_isr();
#asm
NEXT1:
   //Receive data available
   BTFSS     PIE1,RCIE
   GOTO      NEXT2
   BTFSC     PIR1,RCIE
#endasm
   RDA_isr();
#asm
NEXT2:
   //Transmit buffer empty
   BTFSS     PIE1,TXIE
   GOTO      EXIT
   BTFSC     PIR1,TXIE
#endasm
   TBE_isr();
#asm
EXIT:
   //Restore registers
   MOVFF   INT_scratch,FSR0L
   MOVFF   INT_scratch+1,FSR0H
   MOVFF   INT_scratch+2,FSR1L
   MOVFF   INT_scratch+3,FSR1H
   MOVFF   INT_scratch+4,FSR2L
   MOVFF   INT_scratch+5,FSR2H
   MOVFF   INT_scratch+6,scratch
   MOVFF   INT_scratch+7,scratch1
   MOVFF   INT_scratch+8,scratch2
   MOVFF   INT_scratch+9,scratch3
   //Here the 'fast' exit.
FEXIT:
   RETFIE  1
   nop
#ENDASM
}

The register saving used here is only about half the amount normally done. Basically the FSR registers, and four scratch locations. These represent the 'total' of registers used in all the other routines.
The 'nop' after the RETFIE 1, is because this was one of the chips with a fault requiring this.

Best Wishes
dmitrboristuk



Joined: 26 Sep 2020
Posts: 51

View user's profile Send private message

PostPosted: Wed Feb 01, 2023 1:25 am     Reply with quote

Hello. Is it possible to access addresses 0x08 and 0x18 to write your own interrupt handler with different priority levels for pic18 devices.
The standard handler uses a lot of things that I don't need, slowing down the work. This can be seen from the assembler listing.
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Wed Feb 01, 2023 3:44 am     Reply with quote

Yes, just use #INT_GLOBAL.

You just do exactly what I show, except you justify the code between
arriving at the fast handler, and it's exit, to fill 16 bytes. Then have
the low priority handler arrive at what is labelled NXT in my code.
Then don't have the low priority code use the fast return stack.

You do realise that if you use 'FAST', the register saving is removed. So
you can just do your own.

So for INT_GLOBAL:
Code:

#include <18F25K20.h>
#device ADC=10

#FUSES NOWDT                    //No Watch Dog Timer

#use delay(crystal=20000000)
#USE RS232(UART1, baud=9600)

int8 W_TEMP;
int8 BSR_TEMP;
int8 STATUS_TEMP;
int8 INT_SCRATCH[10];

#BIT RBIF=getenv("BIT:RBIF")
#BIT TMR2IF=getenv("BIT:TMR2IF")
#BIT TMR2IE=getenv("BIT:TMR2IE")
#BIT RCIF=getenv("BIT:RCIF")
#BIT RCIE=getenv("BIT:RCIE")
#BIT TXIF=getenv("BIT:TXIF")
#BIT TXIE=getenv("BIT:TXIE")

#BYTE FSR0L=getenv("SFR:FSR0L")
#BYTE FSR1L=getenv("SFR:FSR1L")
#BYTE FSR2L=getenv("SFR:FSR2L")
#BYTE FSR0H=getenv("SFR:FSR0H")
#BYTE FSR1H=getenv("SFR:FSR1H")
#BYTE FSR2H=getenv("SFR:FSR2H")

#BYTE BSR=getenv("SFR:BSR")
#BYTE STATUS=getenv("SFR:STATUS")

#byte PORTB=getenv("SFR:PORTB")

void TBE_isr(void);
void RDA_isr(void);
void TIMER2_isr(void); //prototypes for the interrup handlers

#SEPARATE
void quad(void)
{
   int B_val;
   //handler code for RBIF
   //Remember this has to save every register it changes, excwpt W, BSR & STATUS
   //fast return stack handles these.
   //The saving must be to a separate location to the one used by the slow
   //code
   //Need to read port B or this cannot clear
   B_val=PORTB;
   
   RBIF=FALSE; 
   #ASM
   RETFIE 1 //restore W, BSR & STATUS and return
   NOP
   #ENDASM
}

#int_global
void myint(void) {
   static int INT_scratch[10];
#ASM
   //Here for maximum speed, I test the RB interrupt - since it is allways
   //enabled, I don't have to test the enable bit
   BTFSS   RBIF

   RETFIE 1 //restore W, BSR & STATUS and return
   NOP
#endasm
   quad();              //Quadrature handler.
#asm
   //Now need to fill space here to 0x18.
   NOP
   NOP
   
   //This is now the low priority interrupt handler entry point
   
   //Now I save the registers needed for the other interrupt handlers
   MOVWF   W_TEMP;
   MOVFF   STATUS,STATUS_TEMP
   MOVFF   BSR,BSR_TEMP; //save these, since fast return used on high priority
   MOVFF   FSR0L,INT_scratch
   MOVFF   FSR0H,INT_scratch+1
   MOVFF   FSR1L,INT_scratch+2
   MOVFF   FSR1H,INT_scratch+3
   MOVFF   FSR2L,INT_scratch+4
   MOVFF   FSR2H,INT_scratch+5
   //Then you need to do every other register your interrupt code can change
   

   
   //Now some low priority interrupts
   BTFSS     TMR2IE
   GOTO      NEXT1
   BTFSC     TMR2IF
#endasm
   TIMER2_isr();
#asm
NEXT1:
   //Receive data available
   BTFSS     RCIE
   GOTO      NEXT2
   BTFSC     RCIF
#endasm
   RDA_isr();
#asm
NEXT2:
   //Transmit buffer empty
   BTFSS     TXIE
   GOTO      EXIT
   BTFSC     TXIF
#endasm
   TBE_isr();
#asm
EXIT:
   //Restore registers
   //Need to do every other register yout code has used here
   
   MOVFF   INT_scratch,FSR0L
   MOVFF   INT_scratch+1,FSR0H
   MOVFF   INT_scratch+2,FSR1L
   MOVFF   INT_scratch+3,FSR1H
   MOVFF   INT_scratch+4,FSR2L
   MOVFF   INT_scratch+5,FSR2H
   MOVFF   BSR_TEMP, BSR
   MOVF    W_TEMP
   MOVFF   STATUS_TEMP,STATUS
   RETFIE  0
   NOP  //Some chips have a bug requiring this NOP. Put here in case.
#ENDASM
}

//dummy handlers for the three low priority interrupts
void TBE_isr(void)
{
   putc('A');
   clear_interrupt(INT_TIMER2);
}
void RDA_isr(void)
{
   int dummy;
   dummy=getc();
   clear_interrupt(INT_RDA);
}

void TIMER2_isr(void)
{
   clear_interrupt(INT_TIMER2);
}

void main()
{
   while(TRUE)
   {
      //TODO: User Code
   }
}
dmitrboristuk



Joined: 26 Sep 2020
Posts: 51

View user's profile Send private message

PostPosted: Wed Feb 01, 2023 5:27 am     Reply with quote

Ttelmah,
Thank you, I'll give it a try soon.

I also take this opportunity to ask you why the compiler for PIC18 does not use fast commands such as ADDWFC, COMF, DECFSZ, BC, BNC, RETURN, RCALL and others, instead they use constructions from several instructions inherent in PIC16
temtronic



Joined: 01 Jul 2010
Posts: 9081
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Wed Feb 01, 2023 7:44 am     Reply with quote

My 'gut' feeling is that CCS programmers used what worked in the past, to get the product 'up and running', reliably. Considering the HUGE numbers of PICs today, trying to 'optimize' code for a handful of PICs would be very time consuming.

As Mr T says ,YOU can modify the code to suit EXACTLY what you want the PIC to do, you don't need to use what CCS supplies.
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Wed Feb 01, 2023 8:36 am     Reply with quote

Several of the instructions mentioned, they do use. Just perhaps not where
you expect. ADDWFC, DECFSZ BNC, and RETURN I have just found in the
listings from some of my code here. They avoid RCALL, because I think
it makes the calculations very difficult with the optimiser. RETURN they use
regularly. How else do you think they get back from routines?. Remember
though that they will always tend to 'inline' code if they can, and the stack
size on most PIC's is so limited that they need to avoid using it if they can,
so they will substitute a GOTO if they possibly can.
DIY'ing things has become less and less needed. Historically it was essential
for much fast code, but now the optimiser tends to get very close to as
good as you can get. Generally unless interrupt handlers are very limited in
what they do, the number of extra registers saved is very small, and
remember that the saving used is normally shared by all routines, If you
have one that needs to be really fast, then just use the FAST option.
temtronic



Joined: 01 Jul 2010
Posts: 9081
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Wed Feb 01, 2023 9:01 am     Reply with quote

also... today's PICs have more memory and run a LOT faster than the original 4MHz ones where we had to cut in assembler and play 'tricks' to get use of every bit of memory and every microsecond of time.
dmitrboristuk



Joined: 26 Sep 2020
Posts: 51

View user's profile Send private message

PostPosted: Thu Feb 02, 2023 12:49 am     Reply with quote

Ttelmah,
Your code is much faster than the standard one! I want to ask, is it necessary to set the RBIP and IPEN bits to high priority with this approach?
Code:
static int INT_scratch[10];
for what, it was already above

Another assembler question: how to use the RETURN command in assembler inserts
if you just write it down, it is highlighted in blue (same as RETURN for C).
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Thu Feb 02, 2023 1:36 am     Reply with quote

Return in C, if it is in a function that has been called, is exactly the same
instruction as RETURN in the assembler. The highlighter knows it as a
C keyword, so makes it blue.
Remember though you cannot use a RETURN inside anything that is
in the global interrupt handler (or called from it). The compiler will
substitute a RETFIE if you do.
Yes, you manually have to set the priority. The enable the standard
enable_interrupts instruction will set.

Remember though to look at the assembler listing for all functions you
use inside the interrupt handlers, and you then need to add saving any
registers these use. Even quite basic things will change other registers,
and if the code is going to work correctly you have to be handling these.
dmitrboristuk



Joined: 26 Sep 2020
Posts: 51

View user's profile Send private message

PostPosted: Thu Feb 02, 2023 3:17 am     Reply with quote

Ttelmah, Thank you, so far no questions
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group