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 CCS Technical Support

Multiple use of the same RAM locations

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
Daniel Brännwik
Guest







Multiple use of the same RAM locations
PostPosted: Fri Aug 08, 2003 11:06 am     Reply with quote

Hello,
I have come across a problem after I implemented a bootloader feature. The bootloader itself works ok but the implementation of it forced me to change a lot in the earlier program in order to have all functions at the proper addresses in flash.
I beleive that the problem has occured due to nested functions calls in the interrupt service routines in combination with use of the #separate and #inline directives.

What occurs is that local variabels in the main loop (and functions called from it) are shared with local variabels in the functions that are called from the irq routines. This leads to random runtime errors since the values in some local variables can be changed from another function if a irq happens simultaneously. I found the problem when I checked the .sym file created at compilation.

Is this a known problem and what can be done to solve it?
My first approach was to make all colliding variables static. This works in most cases but it takes a lot of extra RAM and is not particulary easy to maintain in the future development process.
Also it does not solve the cases where I have input data to a functions, like "void function1 (byte var)". "Var" can also collide with another local variable somewhere else.
The same applies for all scratch (temporary) variables that the compiler uses for doing operations invisible to the user.

Is there a way to reserve RAM where the compiler should put the local variables of a function? I have looked at the #reserve directive but it seems to do the opposite as what I want to achieve?
Any suggestion how to deal with this problem is highly appreciated. As always I have found this problem when time to delivery is very short.

I use PCW 3.164 and PIC16F877A.
___________________________
This message was ported from CCS's old forum
Original Post ID: 144516826
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

Re: Multiple use of the same RAM locations
PostPosted: Fri Aug 08, 2003 11:33 am     Reply with quote

:=What occurs is that local variabels in the main loop (and functions called from it) are shared with local variabels in the functions that are called from the irq routines.
------------------------------------------------------

Tradition is to keep irq routines short and set flags that
are polled in the main loop. Can you re-write your code to
do it this way ?
___________________________
This message was ported from CCS's old forum
Original Post ID: 144516827
Neutone



Joined: 08 Sep 2003
Posts: 839
Location: Houston

View user's profile Send private message

Re: Multiple use of the same RAM locations
PostPosted: Fri Aug 08, 2003 12:05 pm     Reply with quote

:=
:=Hello,
:=I have come across a problem after I implemented a bootloader feature. The bootloader itself works ok but the implementation of it forced me to change a lot in the earlier program in order to have all functions at the proper addresses in flash.
:=I beleive that the problem has occured due to nested functions calls in the interrupt service routines in combination with use of the #separate and #inline directives.
:=
:=What occurs is that local variabels in the main loop (and functions called from it) are shared with local variabels in the functions that are called from the irq routines. This leads to random runtime errors since the values in some local variables can be changed from another function if a irq happens simultaneously. I found the problem when I checked the .sym file created at compilation.
:=
:=Is this a known problem and what can be done to solve it?
:=My first approach was to make all colliding variables static. This works in most cases but it takes a lot of extra RAM and is not particulary easy to maintain in the future development process.
:=Also it does not solve the cases where I have input data to a functions, like "void function1 (byte var)". "Var" can also collide with another local variable somewhere else.
:=The same applies for all scratch (temporary) variables that the compiler uses for doing operations invisible to the user.
:=
:=Is there a way to reserve RAM where the compiler should put the local variables of a function? I have looked at the #reserve directive but it seems to do the opposite as what I want to achieve?
:=Any suggestion how to deal with this problem is highly appreciated. As always I have found this problem when time to delivery is very short.
:=
:=I use PCW 3.164 and PIC16F877A.

Concider the following code example.
RegisterMap is declared and located on the bank boundry to eliminate index conversion for arrays not located at start of bank.

All the other variables are located relative to the first variable declaration. bits, bytes, words and even another array.

When writing a function that uses a number of variables locating the variables in this manner will insure that they are all in the same bank of memory. This will eliminate a lot of the need for the compiler to perform bank switching. This can be observed in the listing file.



#include "18F452.h"

int8 RegisterMap[256]; // To provide access to Configuration map in an 8 bit format
#locate RegisterMap = 0x100 //Through 0x1FF(MAX)

int16 RegisterMap_16_Bit[128]; // To provide access to Configuration map in a 16 bit format
#locate RegisterMap_16_Bit = RegisterMap //Through 0x1FF(MAX)

int32 led_Status;
#locate led_Status = RegisterMap +1
#bit led_0 = led_Status.0
#bit led_1 = led_Status.1
#bit led_2 = led_Status.2

void main()
{ led_0=1;
led_1=0;
led_Status = 0xAFAF;
RegisterMap_16_Bit[0] = 1;
}

here is another example that I use to create/verify CRC16 checks on modbus packets.


#inline // Inline code runs faster
void Make_Check_CRC(void) // This function can be used to create or check CRC
{ int8 msgndx, msglen, crcdata, c; // Message index, length and indexed value
int16 crcacc; // CRC accumuliator
#byte crcacc_lo = crcacc // Alias name for CRC accumuliator low byte
#byte crcacc_hi = crcacc + 1 // Alias name for CRC accumuliator high byte
#bit LS_BIT_crcdata = crcdata.0 // Alias name for bit zero of data value copy
#bit LS_BIT_crcacc = crcacc.0 // Alias name for bit zero of CRC accumuliator
good_crc = 0; // Assume this packet is bad
//msglen // Note the packet length
crcacc = 0xFFFF; // Initialize the CRC accumuliator
Packet_Buffer_Index = 0; // First byte indexed as zero
while(Packet_Buffer_Index < Packet_Buffer_Byte_Count) // Last byte indexed as length -1
{ crcdata = PacketBuffer[Packet_Buffer_Index]; // Use direct addressing on copy of data value
for(Bit_Count=8;Bit_Count>0;Bit_Count--) // Process the data bits
{ if(LS_BIT_crcdata != LS_BIT_crcacc) // Should XOR be performed
crcacc = (crcacc >> 1)^0xA001; // Shift CRC 1 bit right load high with 0 then XOR
else // No XOR performed
crcacc = crcacc >> 1; // Shift CRC 1 bit right load high with 0
crcdata = crcdata >> 1; // Shift data to next bit
} // Processed all bits of byte
Packet_Buffer_Index++; // Advance index to the next byte
} // Solved values for CRC
if((crcacc_lo == PacketBuffer[Packet_Buffer_Byte_Count]) && (crcacc_hi == PacketBuffer[Packet_Buffer_Byte_Count]))
{ good_crc = 1; // The packet recieved was good
ChanAErrors = 0;
}
}
___________________________
This message was ported from CCS's old forum
Original Post ID: 144516830
Daniel Brännwik
Guest







Re: Multiple use of the same RAM locations
PostPosted: Sat Aug 09, 2003 2:24 am     Reply with quote

:=:=What occurs is that local variabels in the main loop (and functions called from it) are shared with local variabels in the functions that are called from the irq routines.
:=------------------------------------------------------
:=
:=Tradition is to keep irq routines short and set flags that
:=are polled in the main loop. Can you re-write your code to
:=do it this way ?

I'm afraid not, since the irq in question is due to a synchronous USART communication where the PIC is slave. Therefore I need to make sure that the answer from the PIC is ready when the master clocks in the data.
(I have another irq from a timer and that is treated as you suggest.)
___________________________
This message was ported from CCS's old forum
Original Post ID: 144516849
Daniel Brännwik
Guest







Re: Multiple use of the same RAM locations
PostPosted: Sat Aug 09, 2003 2:26 am     Reply with quote

<font face="Courier New" size=-1>:=Concider the following code example.
:=RegisterMap is declared and located on the bank boundry to eliminate index conversion for arrays not located at start of bank.
:=
:=All the other variables are located relative to the first variable declaration. bits, bytes, words and even another array.
:=
:=When writing a function that uses a number of variables locating the variables in this manner will insure that they are all in the same bank of memory. This will eliminate a lot of the need for the compiler to perform bank switching. This can be observed in the listing file.
:=
:=
:=
:=#include "18F452.h"
:=
:=int8 RegisterMap[256]; // To provide access to Configuration map in an 8 bit format
:=#locate RegisterMap = 0x100 //Through 0x1FF(MAX)
:=
:=int16 RegisterMap_16_Bit[128]; // To provide access to Configuration map in a 16 bit format
:=#locate RegisterMap_16_Bit = RegisterMap //Through 0x1FF(MAX)
:=
:=int32 led_Status;
:=#locate led_Status = RegisterMap +1
:=#bit led_0 = led_Status.0
:=#bit led_1 = led_Status.1
:=#bit led_2 = led_Status.2
:=
:=void main()
:={ led_0=1;
:= led_1=0;
:= led_Status = 0xAFAF;
:= RegisterMap_16_Bit[0] = 1;
:=}
:=
:=here is another example that I use to create/verify CRC16 checks on modbus packets.
:=
:=
:=#inline // Inline code runs faster
:=void Make_Check_CRC(void) // This function can be used to create or check CRC
:={ int8 msgndx, msglen, crcdata, c; // Message index, length and indexed value
:= int16 crcacc; // CRC accumuliator
:= #byte crcacc_lo = crcacc // Alias name for CRC accumuliator low byte
:= #byte crcacc_hi = crcacc + 1 // Alias name for CRC accumuliator high byte
:= #bit LS_BIT_crcdata = crcdata.0 // Alias name for bit zero of data value copy
:= #bit LS_BIT_crcacc = crcacc.0 // Alias name for bit zero of CRC accumuliator
:= good_crc = 0; // Assume this packet is bad
:= //msglen // Note the packet length
:= crcacc = 0xFFFF; // Initialize the CRC accumuliator
:= Packet_Buffer_Index = 0; // First byte indexed as zero
:= while(Packet_Buffer_Index < Packet_Buffer_Byte_Count) // Last byte indexed as length -1
:= { crcdata = PacketBuffer[Packet_Buffer_Index]; // Use direct addressing on copy of data value
:= for(Bit_Count=8;Bit_Count>0;Bit_Count--) // Process the data bits
:= { if(LS_BIT_crcdata != LS_BIT_crcacc) // Should XOR be performed
:= crcacc = (crcacc >> 1)^0xA001; // Shift CRC 1 bit right load high with 0 then XOR
:= else // No XOR performed
:= crcacc = crcacc >> 1; // Shift CRC 1 bit right load high with 0
:= crcdata = crcdata >> 1; // Shift data to next bit
:= } // Processed all bits of byte
:= Packet_Buffer_Index++; // Advance index to the next byte
:= } // Solved values for CRC
:= if((crcacc_lo == PacketBuffer[Packet_Buffer_Byte_Count]) && (crcacc_hi == PacketBuffer[Packet_Buffer_Byte_Count]))
:= { good_crc = 1; // The packet recieved was good
:= ChanAErrors = 0;
:= }
:=}

Thank you for your answer. I have found the #locate directive now and applied it on the variables in the irq functions. My program seems to be running fine now.
However I still suspect I have a potential problem left. It concerns the scratch variables that the compiler uses for internal use (for example when doing a division). When examining the symbol file (*.sym) I see that some scratch variables in the irq functions are located on the same address as other variables in the main loop. Even though the likeliness for a collision is reduced considerably now I think there's still a chance that it will happen.
Any idea how to control the location of these scratch variables?
</font>
___________________________
This message was ported from CCS's old forum
Original Post ID: 144516850
R.J.Hamlett
Guest







Re: Multiple use of the same RAM locations
PostPosted: Sat Aug 09, 2003 7:33 am     Reply with quote

:=<font face="Courier New" size=-1>:=Concider the following code example.
:=:=RegisterMap is declared and located on the bank boundry to eliminate index conversion for arrays not located at start of bank.
:=:=
:=:=All the other variables are located relative to the first variable declaration. bits, bytes, words and even another array.
:=:=
:=:=When writing a function that uses a number of variables locating the variables in this manner will insure that they are all in the same bank of memory. This will eliminate a lot of the need for the compiler to perform bank switching. This can be observed in the listing file.
:=:=
:=:=
:=:=
:=:=#include "18F452.h"
:=:=
:=:=int8 RegisterMap[256]; // To provide access to Configuration map in an 8 bit format
:=:=#locate RegisterMap = 0x100 //Through 0x1FF(MAX)
:=:=
:=:=int16 RegisterMap_16_Bit[128]; // To provide access to Configuration map in a 16 bit format
:=:=#locate RegisterMap_16_Bit = RegisterMap //Through 0x1FF(MAX)
:=:=
:=:=int32 led_Status;
:=:=#locate led_Status = RegisterMap +1
:=:=#bit led_0 = led_Status.0
:=:=#bit led_1 = led_Status.1
:=:=#bit led_2 = led_Status.2
:=:=
:=:=void main()
:=:={ led_0=1;
:=:= led_1=0;
:=:= led_Status = 0xAFAF;
:=:= RegisterMap_16_Bit[0] = 1;
:=:=}
:=:=
:=:=here is another example that I use to create/verify CRC16 checks on modbus packets.
:=:=
:=:=
:=:=#inline // Inline code runs faster
:=:=void Make_Check_CRC(void) // This function can be used to create or check CRC
:=:={ int8 msgndx, msglen, crcdata, c; // Message index, length and indexed value
:=:= int16 crcacc; // CRC accumuliator
:=:= #byte crcacc_lo = crcacc // Alias name for CRC accumuliator low byte
:=:= #byte crcacc_hi = crcacc + 1 // Alias name for CRC accumuliator high byte
:=:= #bit LS_BIT_crcdata = crcdata.0 // Alias name for bit zero of data value copy
:=:= #bit LS_BIT_crcacc = crcacc.0 // Alias name for bit zero of CRC accumuliator
:=:= good_crc = 0; // Assume this packet is bad
:=:= //msglen // Note the packet length
:=:= crcacc = 0xFFFF; // Initialize the CRC accumuliator
:=:= Packet_Buffer_Index = 0; // First byte indexed as zero
:=:= while(Packet_Buffer_Index < Packet_Buffer_Byte_Count) // Last byte indexed as length -1
:=:= { crcdata = PacketBuffer[Packet_Buffer_Index]; // Use direct addressing on copy of data value
:=:= for(Bit_Count=8;Bit_Count>0;Bit_Count--) // Process the data bits
:=:= { if(LS_BIT_crcdata != LS_BIT_crcacc) // Should XOR be performed
:=:= crcacc = (crcacc >> 1)^0xA001; // Shift CRC 1 bit right load high with 0 then XOR
:=:= else // No XOR performed
:=:= crcacc = crcacc >> 1; // Shift CRC 1 bit right load high with 0
:=:= crcdata = crcdata >> 1; // Shift data to next bit
:=:= } // Processed all bits of byte
:=:= Packet_Buffer_Index++; // Advance index to the next byte
:=:= } // Solved values for CRC
:=:= if((crcacc_lo == PacketBuffer[Packet_Buffer_Byte_Count]) && (crcacc_hi == PacketBuffer[Packet_Buffer_Byte_Count]))
:=:= { good_crc = 1; // The packet recieved was good
:=:= ChanAErrors = 0;
:=:= }
:=:=}
:=
:=Thank you for your answer. I have found the #locate directive now and applied it on the variables in the irq functions. My program seems to be running fine now.
:=However I still suspect I have a potential problem left. It concerns the scratch variables that the compiler uses for internal use (for example when doing a division). When examining the symbol file (*.sym) I see that some scratch variables in the irq functions are located on the same address as other variables in the main loop. Even though the likeliness for a collision is reduced considerably now I think there's still a chance that it will happen.
:=Any idea how to control the location of these scratch variables?
:=</font>
The scratch variables are normally saved by the IRQ handler. Look at the assembler for the standard handler, and you should find it saves the contents of these locations. :-)

Best Wishes
___________________________
This message was ported from CCS's old forum
Original Post ID: 144516854
Daniel Brännwik
Guest







Re: Multiple use of the same RAM locations
PostPosted: Sat Aug 09, 2003 8:15 am     Reply with quote

:=The scratch variables are normally saved by the IRQ handler. Look at the assembler for the standard handler, and you should find it saves the contents of these locations. :-)
:=
:=Best Wishes

I'm not sure what you mean. But I think the reason for my problems is that the compiler doesn't understand that the functions called from my isr are indeed connected to the isr.
I have, as I mentioned in an earlier post used #inline, #separate and #org statements extensively in order to get the program to work with my bootloader. I think this is the cause of the problems. Below is a snippet from my .sym file which illustrates what I mean:

0CD main2.ch_no - called from main
0CD @DIV88.@DIV88.P1
0CD @MUL88.@MUL88.P2
0CD main.@SCRATCH - used in main
0CD read_parameters.@SCRATCH - called from the isr!
0CD cmd_decode.@SCRATCH - called from the isr!

If for example "ch_no" in main2 is used when a irq comes its content will be corrupted. Or am I missing something?
___________________________
This message was ported from CCS's old forum
Original Post ID: 144516855
R.J.Hamlett
Guest







Re: Multiple use of the same RAM locations
PostPosted: Sat Aug 09, 2003 2:34 pm     Reply with quote

:=:=The scratch variables are normally saved by the IRQ handler. Look at the assembler for the standard handler, and you should find it saves the contents of these locations. :-)
:=:=
:=:=Best Wishes
:=
:=I'm not sure what you mean. But I think the reason for my problems is that the compiler doesn't understand that the functions called from my isr are indeed connected to the isr.
:=I have, as I mentioned in an earlier post used #inline, #separate and #org statements extensively in order to get the program to work with my bootloader. I think this is the cause of the problems. Below is a snippet from my .sym file which illustrates what I mean:
:=
:=0CD main2.ch_no - called from main
:=0CD @DIV88.@DIV88.P1
:=0CD @MUL88.@MUL88.P2
:=0CD main.@SCRATCH - used in main
:=0CD read_parameters.@SCRATCH - called from the isr!
:=0CD cmd_decode.@SCRATCH - called from the isr!
:=
:=If for example "ch_no" in main2 is used when a irq comes its content will be corrupted. Or am I missing something?

This is not a problem.
If you look at the .asm file (from the windows system, select 'view', 'C/ASM List', and then look through this for the interrupt handler code (where this is, depends on the chip, but on a 16F, it is at address 4), then look carefully at the code that follows, you will see something like:
0004: MOVWF 7F
0005: SWAPF 03,W
0006: CLRF 03
0007: MOVWF 21
0008: MOVF 0A,W
0009: MOVWF 20
000A: CLRF 0A
000B: MOVF 04,W
000C: MOVWF 22
000D: MOVF 77,W
000E: MOVWF 23
000F: MOVF 78,W
0010: MOVWF 24
0011: MOVF 79,W
0012: MOVWF 25
0013: MOVF 7A,W
0014: MOVWF 26
0015: MOVF 7B,W
0016: MOVWF 27

Now this does the 'normal' interrupt essentials (of saving WREG, STATUS, and the BSR, but then goes on to copy the registers at locations 77 to 7B, into storage at locations 23 to 27. On this particular code, the locations at 77 to 7B, are the 'scratch' locations (these will move according to the code). The inverse code at the end of the interrupt handler, restores these locations. Hence the main code, and the ISR, can use the same scratch locations without a problem. In your case, the 'scratch' locations are up round 0CD, so you should find these being saved into the memory area referred to in the symbol listing as '@INTERRUPT_AREA'.

Your interrupt routine (unless you are writing your own 'int_global' handler, is not a 'stand alone' routine. When an interrupt occurs, 'int_global' is called, which saves the registers, and tests which interrupt is involved. It then calls the actual interrupt handler, in this 'saved' enviroment, then clears the interrupt flag, and restores the registers before exiting. The time involved in this can be significant (dozens of machine cycles added to the interrupt handler), hence if you want the fastest speed, and know you are not going to use the scratch area, you can write your own interrupt handler, and get rid of this time delay.
However for you, this overhead, ensures that the scratch area is not corrupted by the interrupt code (unless something else overwrites the storage used for these values...).

Best Wishes
___________________________
This message was ported from CCS's old forum
Original Post ID: 144516858
Michael Thompson
Guest







Re: Multiple use of the same RAM locations
PostPosted: Mon Aug 11, 2003 12:44 pm     Reply with quote

If one uses the #int pragma to specify that the function following this pragma is an interrupt function, the compiler will assign the local variables and scratch registers as static variables since the CCS C compiler does not use a stack for local function variables as does standard C. If not using the CCS compiler's #int pragma, then defining the interrupt function's variables as "static" would keep the variable locations from being reused. However, scratch registers would still be a problem.

Regards,
Mike
___________________________
This message was ported from CCS's old forum
Original Post ID: 144516892
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