View previous topic :: View next topic |
Author |
Message |
guest Guest
|
trying to force scratch variables to specific locations |
Posted: Tue Aug 22, 2006 1:47 pm |
|
|
Hello,
I am trying to force the scratch variables generated by the compiler to be in specificed locations. By default, it puts the scratch variables randomly.
I have tried "#locate @SCRATCH=0x44" (for example) but the compiler barfs on the "@" sign.
Thanks!
Bill |
|
|
Ttelmah Guest
|
|
Posted: Tue Aug 22, 2006 2:51 pm |
|
|
There are two distinctly different types of scratch variable used by CCS. The first are the true 'system' scratch variables. These are located at the bottom of memory, and are global/static in nature. The location for these, can be defined from the device editor, under the 'memory' tab, and they will be located at the defined point, and grow upwards, normally occupying just five locations.
The second type, are _dynamic_ scratch variables, and these are allocated the same way as local variables, using the memory 'heap'. Their location, will depend on the memory available to the subroutine calling the routine using them. Trying to locate these to a fixed location, will result in a massive waste of memory, since the same memory can be re-used, when the particular routine is not being called. So:
Code: |
077 EEPROM_PUT.addr
077 EEPROM_GET.@SCRATCH
077 @PRINTF_LU_334.@SCRATCH
077 @PRINTF_L32D_334FPFPF.@SCRATCH
077 @FLT.@SCRATCH
|
This program has the same location, being used for five scratch variables, and a variable inside a subroutine...
Best Wishes |
|
|
Guest
|
|
Posted: Wed Aug 23, 2006 12:05 am |
|
|
Thanks, Ttelmah,
Unfortunately, I definitely have the need to locate the local scratch registers as fixed, even though there will be a waste of memory.
Bill |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Wed Aug 23, 2006 1:11 am |
|
|
I can't think of any reason why you would want to locate the local scratch registers to a fixed address. Just imagine you were not using a PIC processor but a processor with a normal stack pointer, than nobody would even consider to locate a variable on the stack to a fixed location because it would be plain impossible. Now you are using a PIC processor where things are organized a bit different, but still the same principles apply and I think it is a very strange idea.
Can you explain why you want to locate a local variable to a fixed memory address? Most likely we than can suggest a more elegant solution to your problem. |
|
|
Guest
|
|
Posted: Fri Aug 25, 2006 1:24 pm |
|
|
Hi Ckielstra,
We have a complex bootloader, and the code in the bootloader uses scratch variables. The problem is that when a new "application" is loaded, the scratch variables used by the application can collide those previously used by the bootloader, unless we can force the bootloader to always use the same locations.
Bill |
|
|
Ttelmah Guest
|
|
Posted: Fri Aug 25, 2006 3:07 pm |
|
|
Er. That shouldn't matter a jot.
When the bootloader is working it uses the memory. Once the bootloader has completed, and the code is running, it uses the same memory. This si normal, and acceptable...
Best Wishes |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Fri Aug 25, 2006 4:40 pm |
|
|
I agree with Ttelmah, the use of the same memory for scratch variables by the bootloader and your main application shouldn't matter. Remember why these variables are called scratch variables... when your function (or bootloader) has finished this memory is free for other uses. |
|
|
Santiago Moronho Arenas
Joined: 18 Oct 2006 Posts: 4 Location: Galicia
|
locating dynamic varaibles |
Posted: Tue Dec 12, 2006 2:06 pm |
|
|
Hey guys/[spam]
I don't now if your are still there with this topic, but I have the same need for locating the _dynamic_ scratch variables to a certain position (bank 0).
Despite of the waste of memory, could you please tell me how can this location be done?
Best regards _________________ Santi - Itelsis |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
Re: locating dynamic varaibles |
Posted: Tue Dec 12, 2006 3:37 pm |
|
|
Santiago,
I still can't think of an application requiring the dynamic variables to be located in a fixed memory location. Tell us more about your problem and I'm sure we can come up with a more elegant solution. |
|
|
Santiago Moronho Arenas
Joined: 18 Oct 2006 Posts: 4 Location: Galicia
|
|
Posted: Wed Dec 13, 2006 9:14 am |
|
|
Ok Ckielstra, I will be very glad to tell you, and take your suggestions in account.
It's a 16LC717 runnng at the 4MHz internal clock, and acting as a slave i2c device at 100Kbit/s. There are two irq enabled: i2c and a task dispatcher timer.
The slave i2c isr has to leave the reception buffer ready in a maximum of 90 instruction cycles, and could happen to interrupt just after the timer interrpt has started. The interrupt manager takes about 40 cycles to get into the isr user's code, and the isr timer is very short: 1 instruction.
The i2c isr is:
Code: |
#int_SSP
void IsrSlaveI2cA() {
//int istate, iincoming; //These local variables have been declared
//as globals to stick them to bank 0.
istate = i2c_isr_state(A); //Reports about recent byte interrupting.
if (istate != 0x00) { //DISCARD ADDRESS
if (istate < 0x80) { //I2C SLAVE RX (MASTER WR)
iincoming = i2c_read(A); //Read first, critical timing.
if (istate == 0x01) {
RegIdx = iincoming; //1st data byte: the index.
FirstIdx = iincoming; //Save. Decides actions PicDDS&E2P
FlagWasI2cMasterWr = 1; //Signal R/W to PicDDS & E2P tasks
}
else
Reg.Buf[RegIdx++]=iincoming; //2nd save & prep next.
}
else { //I2C SLAVE TX (MASTER RD)
i2c_write(A, Reg.Buf[RegIdx++]);
FlagWasI2cMasterWr = 0; //Signal R/W PicDDS & E2P tasks
}
}
}
|
Thiose dynamic scratch variables make the code longer because os bank swithcing. The system is now working, but as far as I have calculated it very close to overflow, so I would loke to have a little bit relaxed.
Best regards! _________________ Santi - Itelsis |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Wed Dec 13, 2006 11:48 am |
|
|
Tweaking your code to get the last bit performance out of it could indeed be a reason for desperate measures. Making the local variables as you did is one way to get more control over memory allocation.
Did you have a look at the symbol table, the *.sym file, for your program? You will see that the compiler tries to locate all dynamic variables at the start of memory, than all other variables are mostly added in sequence thay appear in your program. For minimizing bank switching it is good practice to put your large buffer variables at the end of the program. Some fiddling and shuffling with where you declare the variables in your program can save a lot of bank switches, in my program this gave me about 10% code size reduction!
In your application saving a few instructions by getting rid of the bank switching is nice, but will it do the job? A much larger improvement can be gained from getting rid of the interrupt routines. Yes, I know, this sounds strange but let me explain. The CCS compiler generates standard interrupt handler code which saves and restores all the registers you might possibly change in an interrupt routine, for a PIC16C717 and compiler v3.249 this is about 60 instructions overhead. Now for very tight timing applications you might be better of by polling the interrupt flags from the main loop, this will reduce the overhead with almost 60 instructions. Just make sure the loop is fast enough and doesn't get stalled by a function like printf.
Code: | #include <16C717.h>
#FUSES HS, NOWDT
#device *=16
#use delay(clock=4000000)
#byte INTCON= 0x000B
#byte PIR1 = 0x000C
#bit T0IF = INTCON.2
#bit SSPIF = PIR1.3
void Handle_Timer0_Interrupt()
{
// Do here your timer stuff
clear_interrupt(INT_TIMER0);
}
void Handle_I2C_Interrupt()
{
// Do here your I2C stuff
clear_interrupt(INT_SSP);
}
void main()
{
enable_interrupts(INT_TIMER0);
enable_interrupts(INT_SSP);
clear_interrupt(INT_TIMER0);
clear_interrupt(INT_SSP);
// enable_interrupts(GLOBAL); Do not enable this line! We call the interrupt service routines from main().
while(1)
{
if (T0IF)
Handle_Timer0_Interrupt();
if (SSPIF)
Handle_I2C_Interrupt();
}
}
|
|
|
|
Santiago Moronho Arenas
Joined: 18 Oct 2006 Posts: 4 Location: Galicia
|
|
Posted: Wed Dec 13, 2006 2:58 pm |
|
|
Thank's for the suggestion, but I'm afraid it's not feasible because there are a lot more things to be done in the main loop of the application (including a 2nd master i2c bus, adc readings, etc)
As far as I've tested now the system is working properly, It's only that I estimate margins are quite tight, and the immediate consequence of an overflow is the MSSP getting stuck until someone clears the SSPOV bit.
So, on one side I'm trying to loosen the tight margins with a faster isr and on the other side I'll put in a monitoring task to recover any eventual overflow.
I've just received a VERY INTERESTING answer from the CCS support:
Quote: | Inside a function you can do:
#locate AUTO=address
And all the local automatically located variables will start at the specified address. |
But I was told to update my PCM compiler version from 4.011 to 4.018. If it works, it's quite nice for this kind of things. In fact I did't even realize that the locate directive can allso be invoked for local variables inside functions, I thought it was only for globals.
So, thank's a lot
Santi _________________ Santi - Itelsis |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Wed Dec 13, 2006 7:47 pm |
|
|
Just two more tips
Make sure the most critical interrupt is serviced first, using the #priority directive.
Consider writing your own interrupt dispatcher code, check the manual for the paragraph on #int_global. Just copy the dispatcher as generated by the CCS compiler and then strip the saving of all registers that are untouched by your code. Not easy to do as this requires manual inspection of your interrupt handler code but for each register save/restore operation you can strip you will save 4 instructions. |
|
|
|