|
|
View previous topic :: View next topic |
Author |
Message |
RLScott
Joined: 10 Jul 2007 Posts: 465
|
Suggestion for scratch registers |
Posted: Mon Nov 23, 2015 10:22 pm |
|
|
I often use #INT_GLOBAL for faster interrupt speed, rather than the CCS interrupt dispatcher. One problem with this is that nothing is saved, unless I do it myself. With the PIC18F parts this is not much of a problem since the hardware saves STATUS, W, and BSR. But not so with the @SCRATCH registers.
It occurs to me that it might be desirable to have the compiler use a separate set of scratch registers during interrupts, assuming there is enough unused RAM to do it. It also might be difficult for functions that are called from both interrupt code and main code. But I would be content if just the #INT_GLOBAL routine used the alternate scratch registers. If I decide to call a function from both the interrupt code and the main code, I can take responsibility for saving the main scratch registers, but that would be a very rare application.
Is there already some option I am not aware of that would provide for using alternate scratch registers in interrupts? So far what I have done is examine the compiled code in the .LST file to see if scratch registers are being employed and then rewrite the code, if possible, to make the use of those registers unnecessary. If I cannot do that, then I do go ahead and save and restore the scratch registers. _________________ Robert Scott
Real-Time Specialties
Embedded Systems Consulting |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Tue Nov 24, 2015 3:16 am |
|
|
Problem is the PIC is very inefficient at using 'movable' registers. Code that accesses an address that can change needs about 10 extra lines versus the same code accessing an address at a fixed location. Hence 'movable scratch' is not going to be efficient in a PIC. What you could do is load a separate maths library (how to do this has been described here), which then creates separate versions for the interrupt and main code, each with their own scratch. This would only leave a very few scratch locations common to both. However this is going to be quite bulky, and generally the scratch locations are few compared to the number of processor registers involved.
I've often wished the PIC had duplicate/swappable primary registers available (as was done years before on several early processors). |
|
|
RLScott
Joined: 10 Jul 2007 Posts: 465
|
|
Posted: Tue Nov 24, 2015 10:44 am |
|
|
It would not be necessary to have run-time movable scratch registers. It would be sufficient if the alternate scratch registers were just used for the in-line code generated in the #INT_GLOBAL function itself. I know there are lots of library functions that probably use scratch registers, and I don't expect those to be the alternate ones. If my interrupt routine is doing things involved enough to need library routines, then I probable have enough time to save and restore the standard scratch registers they might use. The real benefit to such an option would be for those very fast interrupt routines that have no need to call library functions. Just a simple 32-bit add and subtract is enough to invoke scratch registers. If those could be done with alternate scratch registers that would be a big help to me. _________________ Robert Scott
Real-Time Specialties
Embedded Systems Consulting |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Tue Nov 24, 2015 12:26 pm |
|
|
You are fractionally missing the point.
If the maths routines are going to use alternative copies of the scratch, the routines themselves have to be duplicated, otherwise they are going to have to use movable registers. Hence the need to load an alternative maths library. Understand that if you tell the compiler to use an alternative library, it only actually loads the routines that are _used_, so loading an extra library would only load the maths functions your code uses in the section involved.
Look at this thread:
<http://www.ccsinfo.com/forum/viewtopic.php?t=25464&start=4>
Which shows how to make the compiler use a separate library for the maths.
Do this, and you will find that the symbol map suddenly has two sets of scratch registers for the div16 routine.... |
|
|
RLScott
Joined: 10 Jul 2007 Posts: 465
|
|
Posted: Tue Nov 24, 2015 1:11 pm |
|
|
As I said, I don't want or expect the math library routines to use alternate scratch registers. I have lots of interrupt code that does not use any math routines (from the library) at all. It does use some math operations, like adding and subtracting 32-bit numbers. I have looked at the compiled code in the LST file and they do not call any math routines. They are compiled into in-line calculations, which also happen to use scratch registers. The compiler could use alternate scratch registers for these in-line calculations without affecting any math library routines. I don't need to do something so drastic as load an entirely separate alternate math library to accomplish this. One work-around, I suppose might be to compile as it current is, then take the compiled code from the LST file and use it to design a #ASM...#ENDASM equivalent for what that code does, except that I will use my own scratch registers instead of the ones the compiler did. Of course this is a lot of work and is not very maintainable, which is why I raised the possibility of just specifying an option that in the #INT_GLOBAL code and _only_ in the #INT_GLOBAL code, ram locations 4,5,6 and 7 would be used instead of 0,1,2,3 (the usual @SCRATCH locations). _________________ Robert Scott
Real-Time Specialties
Embedded Systems Consulting |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Wed Nov 25, 2015 2:56 am |
|
|
Just use the example I showed, and see what it does.
Separate maths routines for the interrupt, and separate scratch. Exactly what you want/is needed. All maths is 'from the library'. 'Maths.c', is not 'the library', it is the extended (things like log') library.
It does what you want. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Wed Nov 25, 2015 4:50 am |
|
|
Let me do a demo of how to use this:
Code: |
#include <18F4520.h>
#fuses XT,NOWDT,NOPROTECT,PUT,BROWNOUT,NOLVP
#use delay(clock=4000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS)
#byte FSR0L=getenv("SFR:FSR0L")
#byte FSR1L=getenv("SFR:FSR1L")
#byte FSR2L=getenv("SFR:FSR2l")
#byte FSR0H=FSR0L+1
#byte FSR1H=FSR1L+1
#byte FSR2H=FSR2L+1
//demo of building a INT_GLOBAL, that handles multiple interrupts (INT_EXT, and INT_TIMER2)
//and uses it's own local scratch - placing as low as possible above the core
//'global' section
#org 0x50,0x400 DEFAULT
//Both handlers now using separate scratch from the main
void int_ext_handler(void)
{
int16 locala,localb,localc;
//INT_EXT interrupt handler
//using separata scratch from the main code
localc=locala/localb; //prove where division scratch is put
clear_interrupt(INT_EXT);
}
void int_timer2_handler(void)
{
//Timer2 handler - doing nothing at the moment, but would use local
//scratch
clear_interrupt(INT_TIMER2);
}
#org default
#int_global
void myint(void) {
static int INT_saves[6];
//Save the fsr'S
#ASM
MOVFF FSR0L,INT_saves //save what registers you need - just demo with FSR's
MOVFF FSR0H,INT_saves+1
MOVFF FSR1L,INT_saves+2
MOVFF FSR1H,INT_saves+3
MOVFF FSR2L,INT_saves+4
MOVFF FSR2H,INT_saves+5
#endasm
//Test for interrupts required
//Since I don't at any point disable the individual interrupts, can save time
//by just checking if they are active, not enabled/disabled.
if (interrupt_active(INT_EXT))
int_ext_handler();
if (interrupt_active(INT_TIMER2))
int_timer2_handler();
//Restore registers - again you need to restore what your code uses.
#ASM
MOVFF INT_saves,FSR0L
MOVFF INT_saves+1,FSR0H
MOVFF INT_saves+2,FSR1L
MOVFF INT_saves+3,FSR1H
MOVFF INT_saves+4,FSR2L
MOVFF INT_saves+5,FSR2H
//Here the 'fast' exit.
FEXIT:
RETFIE 1 //use retfie 1 - ensures W etc., are restored
#ENDASM
}
//=============================
void main(void)
{
int16 a,b,c;
enable_interrupts(INT_TIMER2);
enable_interrupts(INT_EXT);
enable_interrupts(GLOBAL);
//maths in the main, to test where scratch gets put
c = a/b;
while(1)
;
}
|
Note also one 'key difference' from the CCS global handler. This will handle _both_ interrupts if both have triggered, only saving the registers once. More efficient. |
|
|
RLScott
Joined: 10 Jul 2007 Posts: 465
|
|
Posted: Wed Nov 25, 2015 8:42 am |
|
|
Thank you for taking the time to prepare this demo for me. I pasted it into my project and compiled it, and this is what I noticed from looking at the resulting LST file:
The interrupt handlers call a version of @@DIV1616 located at 0x50 and the main program calls a version of @@DIV1616 located at 0x402. Looking at the code at 0x50 and 0x402, they are indeed very similar. But both versions start out by clearing RAM locations 0, 1, 2, and 3 (@SCRATCH in the symbol table). How is that not a problem? Supposedly the 0x402 version of @@DIV1616 could be interrupted and the 0x50 version given control, thus corrupting the 0x402's use of these four registers.
Then I applied the #ORG statements to my current code to further demonstrate the problem. Around my #INT_GLOBAL routine I put:
Code: | #org 0x150,0x400 DEFAULT
#INT_GLOBAL
void isr(void)
{
//..lots of code snipped..
TXREG = txdata[tdcount-1];
//..lots of code snipped
#asm
RETFIE 1 ;..fast return restores STATUS, W, and BSR
#endasm
}
#org DEFAULT |
and in my main program I also had:
Code: | TXREG = txdata[tdcount-1]; |
Looking at the compiled code, the interrupt routine had
Code: | 0010: MOVLW 01
0012: SUBWF 1F,W
0014: CLRF 03
0016: ADDLW 1A
0018: MOVWF FE9
001A: MOVLW 00
001C: ADDWFC 03,W
001E: MOVWF FEA
0020: MOVFF FEF,FAD |
and the main program had
Code: | 051E: MOVLW 01
0520: SUBWF 1F,W
0522: CLRF 03
0524: ADDLW 1A
0526: MOVWF FE9
0528: MOVLW 00
052A: ADDWFC 03,W
052C: MOVWF FEA
052E: MOVFF FEF,FAD |
As you can see, both blocks of code used the same temporary scratch register at 0x03. This does not even involve any maths at all, just ordinary in-line array access. It is this kind of conflict with scratch registers I wish to prevent, and this #ORG method does not solve it. (I realize there is also a conflict with the use of the FSR registers, and that is a separate issue where I would have to explicitly save them as you did in your example. I just grabbed this code as an example. The only thing I am focusing on right now is the conflict with 0x03.) _________________ Robert Scott
Real-Time Specialties
Embedded Systems Consulting |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Wed Nov 25, 2015 9:41 am |
|
|
That's why I always save those four registers....
You started by asking about maths scratch, which is what I answered.
These are the general temporary scratch variables.
Now technically it should be possible to relocate these with #locate.
This was an ability that worked at one time, then stopped, then started again, and is not being accepted by the current compilers.
The syntax was:
#locate auto=<address>
I'd suggest a quick email to CCS, pointing out that this ability is not working. |
|
|
RLScott
Joined: 10 Jul 2007 Posts: 465
|
|
Posted: Wed Nov 25, 2015 10:25 am |
|
|
Ttelmah wrote: | That's why I always save those four registers....
You started by asking about maths scratch, which is what I answered.
These are the general temporary scratch variables.
Now technically it should be possible to relocate these with #locate.
This was an ability that worked at one time, then stopped, then started again, and is not being accepted by the current compilers.
The syntax was:
#locate auto=<address>
I'd suggest a quick email to CCS, pointing out that this ability is not working. |
Will do. Thanks for pointing that out. It does indeed look like just what I want, if would only work. Right now it causes the next line of code to error with "Expecting identifier". _________________ Robert Scott
Real-Time Specialties
Embedded Systems Consulting |
|
|
RLScott
Joined: 10 Jul 2007 Posts: 465
|
|
Posted: Wed Nov 25, 2015 3:12 pm |
|
|
After reporting the problem to support, I got a very prompt and useful response. Here is the whole story on the #locate auto directive:
Despite what the help file says, the correct syntax is:
Code: | void somefunction(...) {
#locate auto=myspace
int a,b,c,d;
//...the rest of the function
} |
where myspace was previously defined with the addressmod() directive. This compiles OK, but it does not solve the problem that we were talking about. What this does is locate the RAM address range where any automatic C variables, like a, b, c, and d above, are assigned. But it does not affect the use of the scratch registers (0x00- 0x03 in previous posts). _________________ Robert Scott
Real-Time Specialties
Embedded Systems Consulting |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Thu Nov 26, 2015 2:19 am |
|
|
I'd query back and point out you want to relocate the scratch. |
|
|
|
|
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
|